aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/settings/configuration.cpp244
-rw-r--r--lib/settings/configuration.h92
-rw-r--r--lib/settings/settings.qbs18
-rw-r--r--smolbote.qbs10
-rw-r--r--test/config/config.qrc5
-rw-r--r--test/config/main.cpp40
-rw-r--r--test/config/poi.cfg1
-rw-r--r--test/test.qbs38
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",
+ ]
+
+ }
+}