diff options
author | Aqua-sama <aqua@iserlohn-fortress.net> | 2018-01-17 13:32:13 +0100 |
---|---|---|
committer | Aqua-sama <aqua@iserlohn-fortress.net> | 2018-01-17 13:32:13 +0100 |
commit | d592ee191e8aedc84c670e6d818fcf97c0bd7a92 (patch) | |
tree | b2bd8413332cd1567b9ae954835a7a4c930de730 | |
parent | Fixed build scripts for use with the mercurial repository (diff) | |
download | smolbote-d592ee191e8aedc84c670e6d818fcf97c0bd7a92.tar.xz |
Configuration creates missing items based on defaults
-rw-r--r-- | data/poi.cfg | 5 | ||||
-rw-r--r-- | lib/settings/configuration.cpp | 233 | ||||
-rw-r--r-- | lib/settings/configuration.h | 50 | ||||
-rw-r--r-- | src/browser.cpp | 5 | ||||
-rw-r--r-- | src/main.cpp | 35 |
5 files changed, 141 insertions, 187 deletions
diff --git a/data/poi.cfg b/data/poi.cfg index 26a4c34..557cda5 100644 --- a/data/poi.cfg +++ b/data/poi.cfg @@ -46,6 +46,11 @@ filter = { path = "~/.config/smolbote/hosts.d"; }; +// Plugin settings +plugins = { + path = "~/.config/smolbote/plugins.d"; +}; + // Profile settings profile = { path = "~/.config/smolbote/Profiles"; diff --git a/lib/settings/configuration.cpp b/lib/settings/configuration.cpp index 06ee0bd..af5af65 100644 --- a/lib/settings/configuration.cpp +++ b/lib/settings/configuration.cpp @@ -7,22 +7,20 @@ */ #include "configuration.h" -#include <libconfig.h++> +#include <QtCore/QStandardPaths> #include <sstream> using namespace libconfig; -Configuration::Configuration() +Configuration::Configuration(const std::string &path, const std::string &home) { m_userCfg = new Config(); -#ifdef C_LIKE_CONFIG + // prettier output m_userCfg->setOptions(Config::OptionSemicolonSeparators | Config::OptionOpenBraceOnSeparateLine); -#endif - // fsync after writing and before closing m_userCfg->setOption(Config::OptionFsync, true); - // make sure the cfgPath is empty - m_userCfgPath = std::string(); + m_userCfgPath = path; + m_homePath = home; m_defaultCfg = new Config(); } @@ -33,14 +31,13 @@ Configuration::~Configuration() delete m_defaultCfg; } -bool Configuration::readUserConfiguration(const std::string &path) +bool Configuration::read() { - m_userCfgPath = path; try { - m_userCfg->readFile(path.c_str()); - } catch(FileIOException) { + m_userCfg->readFile(m_userCfgPath.c_str()); + } catch(const FileIOException &e) { return false; - } catch(ParseException) { + } catch(const ParseException &e) { return false; } return true; @@ -50,30 +47,27 @@ bool Configuration::parse(const std::string &contents) { try { m_userCfg->readString(contents); - } catch(ParseException) { + } catch(const ParseException &e) { return false; } return true; } -bool Configuration::writeUserConfiguration(const std::string &path) +bool Configuration::writeIfNeeded(const std::string &path) { - m_userCfgPath = path; - try { - m_userCfg->writeFile(m_userCfgPath.c_str()); - } catch(FileIOException) { - return false; - } - return true; -} + if(!path.empty()) + m_userCfgPath = path; + + if(!changed) + return true; -bool Configuration::writeIfNeeded() -{ try { m_userCfg->writeFile(m_userCfgPath.c_str()); - } catch(FileIOException) { + } catch(const FileIOException &e) { return false; } + + changed = false; return true; } @@ -81,7 +75,7 @@ bool Configuration::parseDefaultConfiguration(const std::string &contents) { try { m_defaultCfg->readString(contents); - } catch(ParseException) { + } catch(const ParseException &e) { return false; } return true; @@ -94,7 +88,8 @@ std::vector<std::string> Configuration::childrenSettings(const char *name) for(const Setting &setting : root) { if(setting.getType() != Setting::TypeGroup) { - groupNames.push_back(setting.getName()); + groupNames.emplace_back(setting.getName()); + //groupNames.push_back(setting.getName()); } } @@ -108,7 +103,8 @@ std::vector<std::string> Configuration::childrenGroups(const char *name) for(const Setting &setting : root) { if(setting.getType() == Setting::TypeGroup) { - groupNames.push_back(setting.getName()); + groupNames.emplace_back(setting.getName()); + //groupNames.push_back(setting.getName()); } } return groupNames; @@ -116,139 +112,109 @@ std::vector<std::string> Configuration::childrenGroups(const char *name) void Configuration::resetValue(const char *path) { - if(m_userCfg->exists(path)) { - Setting &v = m_userCfg->lookup(path); - switch(v.getType()) { - case Setting::TypeNone: - break; - case Setting::TypeInt: - v = static_cast<int>(m_defaultCfg->lookup(path)); - break; - case Setting::TypeInt64: - v = static_cast<long>(m_defaultCfg->lookup(path)); - break; - case Setting::TypeFloat: - v = static_cast<double>(m_defaultCfg->lookup(path)); - break; - case Setting::TypeString: - v = static_cast<const char *>(m_defaultCfg->lookup(path)); - break; - case Setting::TypeBoolean: - v = static_cast<bool>(m_defaultCfg->lookup(path)); - break; - case Setting::TypeGroup: - break; - case Setting::TypeArray: - break; - case Setting::TypeList: - break; + if(!m_userCfg->exists(path) && m_defaultCfg->exists(path)) { + // entry doesn't exist, create path to it + // split path into array of items, sep=. + std::string s(path); + std::vector<std::string> pathTokens; + std::string::size_type prev_pos = 0, pos = 0; + while((pos = s.find('.', pos)) != std::string::npos) { + std::string substring(s.substr(prev_pos, pos - prev_pos)); + pathTokens.push_back(substring); + prev_pos = ++pos; } - } -} + pathTokens.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word -template <typename T> -std::optional<T> Configuration::value(const char *path) const -{ - // if setting doesn't exist, give back a nullopt - if(!m_userCfg->exists(path)) { - return std::nullopt; + s.clear(); + // iterate through items, creating the path along the way + for(const std::string &t : pathTokens) { + Setting &v = m_userCfg->lookup(s); + Setting &d = m_defaultCfg->lookup(s).lookup(t); + + if(!v.exists(t)) { + v.add(t, d.getType()); + } + + if(s.empty()) + s.append(t); + else + s.append('.' + t); + } } - // setting was found - const Setting &setting = m_userCfg->lookup(path); - std::optional<T> r; + // if the path doesn't exist in the defaultCfg, this will crash - // cast depending on type - // type checks are done during compile time - switch(setting.getType()) { + // entry exists, reset it + Setting &v = m_userCfg->lookup(path); + switch(v.getType()) { case Setting::TypeNone: - r = std::nullopt; break; - case Setting::TypeInt: - // int, unsigned int, long, unsigned long - if constexpr(std::is_same_v<T, std::string>) { - r = std::optional<std::string>(std::to_string(static_cast<int>(setting))); - } else if constexpr(std::is_same_v<T, int> || std::is_same_v<T, unsigned int> || std::is_same_v<T, long> || std::is_same_v<T, unsigned long>) { - r = std::optional<T>(static_cast<T>(setting)); - } else { - r = std::nullopt; - } + v = static_cast<int>(m_defaultCfg->lookup(path)); break; - case Setting::TypeInt64: - // int, unsigned int; long long, unsigned long long - if constexpr(std::is_same_v<T, std::string>) { - r = std::optional<std::string>(std::to_string(static_cast<long long>(setting))); - } else if constexpr(std::is_same_v<T, int> || std::is_same_v<T, unsigned int> || std::is_same_v<T, long long> || std::is_same_v<T, unsigned long long>) { - r = std::optional<T>(static_cast<T>(setting)); - } else { - r = std::nullopt; - } + v = static_cast<long>(m_defaultCfg->lookup(path)); break; - case Setting::TypeFloat: - // float, double - if constexpr(std::is_same_v<T, std::string>) { - r = std::optional<std::string>(std::to_string(static_cast<float>(setting))); - } else if constexpr(std::is_same_v<T, float> || std::is_same_v<T, double>) { - r = std::optional<T>(static_cast<T>(setting)); - } else { - r = std::nullopt; - } + v = static_cast<double>(m_defaultCfg->lookup(path)); break; - case Setting::TypeString: - // const char*, std::string - if constexpr(std::is_same<T, std::string>::value) { - r = std::optional<std::string>(static_cast<const char *>(setting)); - } else { - r = std::nullopt; - } + v = static_cast<const char *>(m_defaultCfg->lookup(path)); break; - case Setting::TypeBoolean: - // bool - if constexpr(std::is_same<T, std::string>::value) { - r = std::optional<std::string>(static_cast<bool>(setting) ? "true" : "false"); - } else if constexpr(std::is_same<T, bool>::value) { - r = std::optional<bool>(static_cast<bool>(setting)); - } else { - r = std::nullopt; - } + v = static_cast<bool>(m_defaultCfg->lookup(path)); break; - case Setting::TypeGroup: - r = std::nullopt; break; - case Setting::TypeArray: - r = std::nullopt; break; - case Setting::TypeList: - r = std::nullopt; break; } - return r; + // if it's a string, it may contain a path + if(v.getType() == Setting::TypeString) { + std::string val(static_cast<const char *>(v)); + v = patchHome(val, m_homePath).c_str(); + } + + changed = true; } -// tell the compiler to export these functions, otherwise you get linking errors -template std::optional<int> Configuration::value<int>(const char *path) const; -template std::optional<unsigned int> Configuration::value<unsigned int>(const char *path) const; -template std::optional<long> Configuration::value<long>(const char *path) const; -template std::optional<unsigned long> Configuration::value<unsigned long>(const char *path) const; +std::string Configuration::castToString(const libconfig::Setting &v) const +{ + // cast depending on type + // type checks are done during compile time + switch(v.getType()) { + case Setting::TypeNone: + return std::string(); + + case Setting::TypeInt: + // int, unsigned int, long, unsigned long + return std::to_string(static_cast<int32_t>(v)); -template std::optional<long long> Configuration::value<long long>(const char *path) const; -template std::optional<unsigned long long> Configuration::value<unsigned long long>(const char *path) const; + case Setting::TypeInt64: + // int, unsigned int; long long, unsigned long long + return std::to_string(static_cast<int64_t>(v)); -template std::optional<float> Configuration::value<float>(const char *path) const; -template std::optional<double> Configuration::value<double>(const char *path) const; + case Setting::TypeFloat: + // float, double + return std::to_string(static_cast<double>(v)); + + case Setting::TypeString: + // const char*, std::string + return std::string(static_cast<const char *>(v)); -template std::optional<bool> Configuration::value<bool>(const char *path) const; + case Setting::TypeBoolean: + // bool + return std::string(static_cast<bool>(v) ? "true" : "false"); -template std::optional<std::string> Configuration::value<std::string>(const char *path) const; + case Setting::TypeGroup: + case Setting::TypeArray: + case Setting::TypeList: + return std::string(); + } +} template <typename T> bool Configuration::setValue(std::string path, const T &val) @@ -319,11 +285,12 @@ template bool Configuration::setValue<bool>(std::string path, const bool &val); template bool Configuration::setValue<std::string>(std::string path, const std::string &val); -std::string &patchHome(std::string &path, const std::string &home) +std::string patchHome(const std::string &path, const std::string &home) { + std::string r = path; const size_t location = path.find('~'); if(location != std::string::npos) { - return path.replace(location, 1, home); + return r.replace(location, 1, home); } - return path; + return r; } diff --git a/lib/settings/configuration.h b/lib/settings/configuration.h index 4fc614e..67ba7b1 100644 --- a/lib/settings/configuration.h +++ b/lib/settings/configuration.h @@ -9,26 +9,20 @@ #ifndef CONFIGURATION_H #define CONFIGURATION_H +#include <libconfig.h++> #include <optional> #include <string> #include <vector> -namespace libconfig -{ -class Config; -class Setting; -} - class Configuration { public: - Configuration(); + explicit Configuration(const std::string &path, const std::string &home); ~Configuration(); - bool readUserConfiguration(const std::string &path); + bool read(); bool parse(const std::string &contents); - bool writeUserConfiguration(const std::string &path); - bool writeIfNeeded(); + bool writeIfNeeded(const std::string &path = std::string()); bool parseDefaultConfiguration(const std::string &contents); @@ -38,39 +32,39 @@ public: void resetValue(const char *path); template <typename T> - std::optional<T> value(const char *path) const; + std::optional<T> value(const char *path) const + { + // if setting doesn't exist, give back a nullopt + if(!m_userCfg->exists(path)) { + const_cast<Configuration *>(this)->resetValue(path); + return value<T>(path); + } + + const libconfig::Setting &v = m_userCfg->lookup(path); + if constexpr(std::is_same_v<T, std::string>) + return std::optional<std::string>(castToString(v)); + else + return std::optional<T>(static_cast<T>(v)); + } template <typename T> bool setValue(std::string path, const T &val); private: + std::string castToString(const libconfig::Setting &v) const; + bool changed = false; + std::string m_homePath; std::string m_userCfgPath; libconfig::Config *m_userCfg, *m_defaultCfg; }; // replace ~ with home -std::string &patchHome(std::string &path, const std::string &home); +std::string patchHome(const std::string &path, const std::string &home); // instantiate functions // this needs to be done because the implementation is in the cpp file -// Settings::value<> -extern template std::optional<int> Configuration::value<int>(const char *path) const; -extern template std::optional<unsigned int> Configuration::value<unsigned int>(const char *path) const; -extern template std::optional<long> Configuration::value<long>(const char *path) const; -extern template std::optional<unsigned long> Configuration::value<unsigned long>(const char *path) const; - -extern template std::optional<long long> Configuration::value<long long>(const char *path) const; -extern template std::optional<unsigned long long> Configuration::value<unsigned long long>(const char *path) const; - -extern template std::optional<float> Configuration::value<float>(const char *path) const; -extern template std::optional<double> Configuration::value<double>(const char *path) const; - -extern template std::optional<bool> Configuration::value<bool>(const char *path) const; - -extern template std::optional<std::string> Configuration::value<std::string>(const char *path) const; - // Settings::setValue<> extern template bool Configuration::setValue<int>(std::string path, const int &val); extern template bool Configuration::setValue<unsigned int>(std::string path, const unsigned int &val); diff --git a/src/browser.cpp b/src/browser.cpp index 43cee5f..1e07a95 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -25,7 +25,10 @@ Browser::Browser(int &argc, char *argv[]) Browser::~Browser() { if(m_config) { - QtConcurrent::run(QThreadPool::globalInstance(), m_config.get(), &Configuration::writeIfNeeded); + //QtConcurrent::run(QThreadPool::globalInstance(), m_config.get(), &Configuration::writeIfNeeded); + QtConcurrent::run([c = m_config.get()]() { + qDebug("Writing configuration: %s", c->writeIfNeeded() ? "ok" : "failed"); + }); } if(m_bookmarksManager) { QtConcurrent::run(QThreadPool::globalInstance(), m_bookmarksManager.get(), &BookmarksWidget::save); diff --git a/src/main.cpp b/src/main.cpp index fb80b7e..0452fdd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,35 +36,18 @@ void bootstrapUserConfig(const std::string &path, Configuration &config) // set firstRun to false so we don't re-init on every run config.setValue<bool>("browser.firstRun", false); - // The .path's need to be overriden because ~ doesn't translate to home - const QString &home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); - - // filter.path - std::string filterPath = config.value<std::string>("filter.path").value(); - config.setValue<std::string>("filter.path", patchHome(filterPath, home.toStdString())); - - // profile.path - std::string profilePath = config.value<std::string>("profile.path").value(); - config.setValue<std::string>("profile.path", patchHome(profilePath, home.toStdString())); - - // bookmarks.path - std::string bookmarksPath = config.value<std::string>("bookmarks.path").value(); - config.setValue<std::string>("bookmarks.path", patchHome(bookmarksPath, home.toStdString())); - - // downloads.path - std::string downloadsPath = config.value<std::string>("downloads.path").value(); - config.setValue<std::string>("downloads.path", patchHome(downloadsPath, home.toStdString())); + config.resetValue("filter.path"); + config.resetValue("profile.path"); + config.resetValue("bookmarks.path"); + config.resetValue("downloads.path"); + config.resetValue("plugins.path"); } int main(int argc, char *argv[]) { // Create application object Browser instance(argc, argv); -#ifdef SMOLBOTE_VERSION instance.setApplicationVersion(SMOLBOTE_VERSION); -#else - instance.setApplicationVersion("1.0.0"); -#endif #ifdef QT_DEBUG QElapsedTimer timer; @@ -122,8 +105,10 @@ int main(int argc, char *argv[]) return 0; } - std::shared_ptr<Configuration> config = std::make_shared<Configuration>(); - config->readUserConfiguration(parser.value(configOption).toStdString()); + std::shared_ptr<Configuration> config = std::make_shared<Configuration>( + parser.value(configOption).toStdString(), + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()); + config->read(); config->parseDefaultConfiguration(readConfig(parser.value(defaultConfigOption))); // check if first run @@ -151,7 +136,7 @@ int main(int argc, char *argv[]) // patch paths bootstrapUserConfig(path.toStdString(), *config); - std::cout << "Generating config to: " << qUtf8Printable(path) << (config->writeUserConfiguration(path.toStdString()) ? " ok" : " failed") << std::endl; + std::cout << "Writing configuration: " << (config->writeIfNeeded(path.toStdString()) ? "ok" : "failed") << std::endl; // exit if this is the only thing we needed to do if(parser.isSet(generateUserConfigOption)) { |