diff options
-rw-r--r-- | lib/settings/configuration.cpp | 244 | ||||
-rw-r--r-- | lib/settings/configuration.h | 92 | ||||
-rw-r--r-- | lib/settings/settings.qbs | 18 | ||||
-rw-r--r-- | smolbote.qbs | 10 | ||||
-rw-r--r-- | test/config/config.qrc | 5 | ||||
-rw-r--r-- | test/config/main.cpp | 40 | ||||
-rw-r--r-- | test/config/poi.cfg | 1 | ||||
-rw-r--r-- | test/test.qbs | 38 |
8 files changed, 447 insertions, 1 deletions
diff --git a/lib/settings/configuration.cpp b/lib/settings/configuration.cpp new file mode 100644 index 0000000..543ea60 --- /dev/null +++ b/lib/settings/configuration.cpp @@ -0,0 +1,244 @@ +/******************************************************************************* + ** + ** nyamp: yet another media player + ** Copyright (C) 2017 Aqua-sama + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#include "configuration.h" +#include <libconfig.h++> +#include <sstream> + +Configuration::Configuration() +{ + m_userCfg = new libconfig::Config(); + m_defaultCfg = new libconfig::Config(); +} + +Configuration::~Configuration() +{ + if(!m_userCfgPath.empty()) { + m_userCfg->writeFile(m_userCfgPath.c_str()); + } + + delete m_userCfg; + delete m_defaultCfg; +} + +bool Configuration::readUserConfiguration(const std::string &path) +{ + m_userCfgPath = path; + try { + m_userCfg->readFile(path.c_str()); + } catch (libconfig::FileIOException) { + return false; + } catch (libconfig::ParseException) { + return false; + } + return true; +} + +bool Configuration::readDefaultConfiguration(const std::string &data) +{ + try { + m_defaultCfg->readString(data); + } catch(libconfig::ParseException) { + return false; + } + return true; +} + +std::vector<std::string> Configuration::children(const char* name) +{ + std::vector<std::string> groupNames; + const libconfig::Setting &root = m_defaultCfg->lookup(name); + + for(auto i = root.begin(); i != root.end(); ++i) { + groupNames.push_back(std::string(i->getName())); + } + + return groupNames; +} + +template<typename T> +std::optional<T> Configuration::value(const char* path) const +{ + libconfig::Config *conf = getConfig(path); + if(conf == nullptr) { + return std::nullopt; + } + + // setting was found + const libconfig::Setting &setting = conf->lookup(path); + std::optional<T> r; + + // cast depending on type + // type checks are done during compile time + switch (setting.getType()) { + case libconfig::Setting::TypeNone: + r = std::nullopt; + break; + + case libconfig::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; + } + break; + + case libconfig::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; + } + break; + + case libconfig::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; + } + break; + + case libconfig::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; + } + break; + + case libconfig::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; + } + break; + + case libconfig::Setting::TypeGroup: + r = std::nullopt; + break; + + case libconfig::Setting::TypeArray: + r = std::nullopt; + break; + + case libconfig::Setting::TypeList: + r = std::nullopt; + break; + } + + return r; +} + +// 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; + +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; + +template std::optional<float> Configuration::value<float>(const char* path) const; +template std::optional<double> Configuration::value<double>(const char* path) const; + +template std::optional<bool> Configuration::value<bool>(const char* path) const; + +template std::optional<std::string> Configuration::value<std::string>(const char* path) const; + +template<typename T> +void Configuration::setValue(std::string path, const T &val) +{ + if(m_userCfg->exists(path)) { + libconfig::Setting &setting = m_userCfg->lookup(path); + // compiler complained about operator= not taking unsinged ints, longs and long longs + if constexpr(std::is_unsigned_v<T> && !std::is_same_v<T, bool>) { + setting = static_cast<typename std::make_signed_t<T>>(val); + } else { + setting = val; + } + return; + } + + // the entry doesn't exist, so we need to create it + // libconfig can't create an entry from a path, we need to get its root, and then add it + + // this will error out if entry being added is not in the default config + + std::istringstream p(path); + libconfig::Setting *userSetting = &m_userCfg->getRoot(); + libconfig::Setting *defaultSetting = &m_defaultCfg->getRoot(); + + std::string i; + while (std::getline(p, i, '.')) { + defaultSetting = &defaultSetting->lookup(i); + // check if user setting exists, if not, create it + if(!userSetting->exists(i)) { + userSetting = &userSetting->add(i, defaultSetting->getType()); + } + } + + if constexpr(std::is_unsigned_v<T> && !std::is_same_v<T, bool>) { + *userSetting = static_cast<typename std::make_signed_t<T>>(val); + } else { + *userSetting = val; + } + +} + +template void Configuration::setValue<int>(std::string path, const int &val); +template void Configuration::setValue<unsigned int>(std::string path, const unsigned int &val); +template void Configuration::setValue<long>(std::string path, const long &val); +template void Configuration::setValue<unsigned long>(std::string path, const unsigned long &val); + +template void Configuration::setValue<long long>(std::string path, const long long &val); +template void Configuration::setValue<unsigned long long>(std::string path, const unsigned long long &val); + +template void Configuration::setValue<float>(std::string path, const float &val); +template void Configuration::setValue<double>(std::string path, const double &val); + +template void Configuration::setValue<bool>(std::string path, const bool &val); + +template void Configuration::setValue<std::string>(std::string path, const std::string &val); + +libconfig::Config *Configuration::getConfig(const char* path) const +{ + if(m_userCfg->exists(path)) { + return m_userCfg; + } else if(m_defaultCfg->exists(path)) { + return m_defaultCfg; + } else { + return nullptr; + } +} diff --git a/lib/settings/configuration.h b/lib/settings/configuration.h new file mode 100644 index 0000000..e0f1872 --- /dev/null +++ b/lib/settings/configuration.h @@ -0,0 +1,92 @@ +/******************************************************************************* + ** + ** nyamp: yet another media player + ** Copyright (C) 2017 Aqua-sama + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include <optional> +#include <vector> + +namespace libconfig { +class Config; +class Setting; +} + +class Configuration +{ +public: + Configuration(); + ~Configuration(); + + bool readUserConfiguration(const std::string &path); + bool readDefaultConfiguration(const std::string &data); + + std::vector<std::string> children(const char *name = ""); + + template<typename T> + std::optional<T> value(const char* path) const; + + template<typename T> + void setValue(std::string path, const T &val); + +private: + libconfig::Config* getConfig(const char* path) const; + + std::string m_userCfgPath; + libconfig::Config *m_userCfg; + libconfig::Config *m_defaultCfg; +}; + +// 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 void Configuration::setValue<int>(std::string path, const int &val); +extern template void Configuration::setValue<unsigned int>(std::string path, const unsigned int &val); +extern template void Configuration::setValue<long>(std::string path, const long &val); +extern template void Configuration::setValue<unsigned long>(std::string path, const unsigned long &val); + +extern template void Configuration::setValue<long long>(std::string path, const long long &val); +extern template void Configuration::setValue<unsigned long long>(std::string path, const unsigned long long &val); + +extern template void Configuration::setValue<float>(std::string path, const float &val); +extern template void Configuration::setValue<double>(std::string path, const double &val); + +extern template void Configuration::setValue<bool>(std::string path, const bool &val); + +extern template void Configuration::setValue<std::string>(std::string path, const std::string &val); + +#endif // SETTINGS_H diff --git a/lib/settings/settings.qbs b/lib/settings/settings.qbs new file mode 100644 index 0000000..e49339f --- /dev/null +++ b/lib/settings/settings.qbs @@ -0,0 +1,18 @@ +import qbs + +Project { + name: "Settings" + + StaticLibrary { + name: "settings" + + Depends { name: "cpp" } + + cpp.cxxLanguageVersion: "c++17" + + files: [ + "configuration.cpp", + "configuration.h" + ] + } +} diff --git a/smolbote.qbs b/smolbote.qbs index dec8bec..6d991eb 100644 --- a/smolbote.qbs +++ b/smolbote.qbs @@ -1,5 +1,6 @@ import qbs import "tools/qbs/GitRepo.js" as GitRepo +import qbs.Probes Project { id: project @@ -18,9 +19,16 @@ Project { references: [ "src/lib/bookmarks/bookmarks.qbs", "src/lib/downloads/downloads.qbs", - "src/lib/navigation/navigation.qbs" + "src/lib/navigation/navigation.qbs", + "lib/settings/settings.qbs", + "test/test.qbs", ] + Probes.PkgConfigProbe { + id: libconfig + name: "libconfig++" + } + CppApplication { id: poi name: "poi" diff --git a/test/config/config.qrc b/test/config/config.qrc new file mode 100644 index 0000000..ab60a1b --- /dev/null +++ b/test/config/config.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>poi.cfg</file> + </qresource> +</RCC> diff --git a/test/config/main.cpp b/test/config/main.cpp new file mode 100644 index 0000000..34d2aec --- /dev/null +++ b/test/config/main.cpp @@ -0,0 +1,40 @@ +/******************************************************************************* + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#include <iostream> +#include "settings/configuration.h" + +#include <QFile> + +int main(int argc, char** argv) +{ + Configuration *settings = new Configuration(); + + // Use QFile so we can easily access qrc: + QFile defaultCfg(":/poi.cfg"); + defaultCfg.open(QIODevice::ReadOnly); + + std::cout << "UserCfg: " << (settings->readUserConfiguration("file.cfg") ? "true" : "false") << std::endl; + std::cout << "DefaultCfg: " << (settings->readDefaultConfiguration(defaultCfg.readAll().toStdString()) ? "true" : "false") << std::endl; + + std::cout << "test = " << settings->value<std::string>("test").value_or("unknown") << std::endl; + + return 0; +} diff --git a/test/config/poi.cfg b/test/config/poi.cfg new file mode 100644 index 0000000..5fd6f29 --- /dev/null +++ b/test/config/poi.cfg @@ -0,0 +1 @@ +test = "value"; diff --git a/test/test.qbs b/test/test.qbs new file mode 100644 index 0000000..3088a90 --- /dev/null +++ b/test/test.qbs @@ -0,0 +1,38 @@ +import qbs +import qbs.Probes + +Project { + name: "Tests" + + Probes.PkgConfigProbe { + id: libconfig + name: "libconfig++" + } + + // References aren't needed because test.qbs is the last reference in smolbote.qbs +// references: [ +// "../lib/settings/settings.qbs" +// ] + + CppApplication { + name: "configtest" + + cpp.includePaths: ['../lib'] + cpp.cxxLanguageVersion: "c++17" + cpp.cxxFlags: libconfig.cflags + cpp.linkerFlags: libconfig.libs + + Depends { + name: "Qt" + versionAtLeast: "5.9.0" + submodules: ["core"] + } + Depends { name: "settings" } + + files: [ + "config/config.qrc", + "config/main.cpp", + ] + + } +} |