diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/settings/configuration.cpp | 233 | ||||
-rw-r--r-- | lib/settings/configuration.h | 50 |
2 files changed, 122 insertions, 161 deletions
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); |