summaryrefslogtreecommitdiff
path: root/src/config
diff options
context:
space:
mode:
Diffstat (limited to 'src/config')
-rw-r--r--src/config/config.cpp127
-rw-r--r--src/config/config.hpp93
2 files changed, 220 insertions, 0 deletions
diff --git a/src/config/config.cpp b/src/config/config.cpp
new file mode 100644
index 0000000..0f3d639
--- /dev/null
+++ b/src/config/config.cpp
@@ -0,0 +1,127 @@
+#include <config/config.hpp>
+#include <utils/tolower.hpp>
+
+#include <iostream>
+#include <cstring>
+
+#include <cstdlib>
+
+using namespace std::string_literals;
+
+extern char** environ;
+
+std::string Config::filename{};
+std::map<std::string, std::string> Config::values{};
+std::vector<t_config_changed_callback> Config::callbacks{};
+
+std::string Config::get(const std::string& option, const std::string& def)
+{
+ auto it = Config::values.find(option);
+
+ if (it == Config::values.end())
+ return def;
+ return it->second;
+}
+
+int Config::get_int(const std::string& option, const int& def)
+{
+ std::string res = Config::get(option, "");
+ if (!res.empty())
+ return std::atoi(res.c_str());
+ else
+ return def;
+}
+
+void Config::set(const std::string& option, const std::string& value, bool save)
+{
+ Config::values[option] = value;
+ if (save)
+ {
+ Config::save_to_file();
+ Config::trigger_configuration_change();
+ }
+}
+
+void Config::connect(const t_config_changed_callback& callback)
+{
+ Config::callbacks.push_back(callback);
+}
+
+void Config::clear()
+{
+ Config::values.clear();
+}
+
+/**
+ * Private methods
+ */
+void Config::trigger_configuration_change()
+{
+ std::vector<t_config_changed_callback>::iterator it;
+ for (it = Config::callbacks.begin(); it < Config::callbacks.end(); ++it)
+ (*it)();
+}
+
+bool Config::read_conf(const std::string& name)
+{
+ if (!name.empty())
+ Config::filename = name;
+
+ std::ifstream file(Config::filename.data());
+ if (!file.is_open())
+ {
+ std::cerr << "Error while opening file " << filename << " for reading: " << strerror(errno) << std::endl;
+ return false;
+ }
+
+ Config::clear();
+
+ auto parse_line = [](const std::string& line, const bool env)
+ {
+ static const auto env_option_prefix = "BIBOUMI_"s;
+
+ if (line == "" || line[0] == '#')
+ return;
+ size_t pos = line.find('=');
+ if (pos == std::string::npos)
+ return;
+ std::string option = line.substr(0, pos);
+ std::string value = line.substr(pos+1);
+ if (env)
+ {
+ auto a = option.substr(0, env_option_prefix.size());
+ if (a == env_option_prefix)
+ option = utils::tolower(option.substr(env_option_prefix.size()));
+ else
+ return;
+ }
+ Config::values[option] = value;
+ };
+
+ std::string line;
+ while (file.good())
+ {
+ std::getline(file, line);
+ parse_line(line, false);
+ }
+
+ char** env_line = environ;
+ while (*env_line)
+ {
+ parse_line(*env_line, true);
+ env_line++;
+ }
+ return true;
+}
+
+void Config::save_to_file()
+{
+ std::ofstream file(Config::filename.data());
+ if (file.fail())
+ {
+ std::cerr << "Could not save config file." << std::endl;
+ return ;
+ }
+ for (const auto& it: Config::values)
+ file << it.first << "=" << it.second << '\n';
+}
diff --git a/src/config/config.hpp b/src/config/config.hpp
new file mode 100644
index 0000000..2ba38cc
--- /dev/null
+++ b/src/config/config.hpp
@@ -0,0 +1,93 @@
+/**
+ * Read the config file and save all the values in a map.
+ * Also, a singleton.
+ *
+ * Use Config::filename = "bla" to set the filename you want to use.
+ *
+ * If you want to exit if the file does not exist when it is open for
+ * reading, set Config::file_must_exist = true.
+ *
+ * Config::get() can then be used to access the values in the conf.
+ *
+ * Use Config::close() when you're done getting/setting value. This will
+ * save the config into the file.
+ */
+
+#pragma once
+
+#include <functional>
+#include <fstream>
+#include <memory>
+#include <vector>
+#include <string>
+#include <map>
+
+typedef std::function<void()> t_config_changed_callback;
+
+class Config
+{
+public:
+ Config() = default;
+ ~Config() = default;
+ Config(const Config&) = delete;
+ Config& operator=(const Config&) = delete;
+ Config(Config&&) = delete;
+ Config& operator=(Config&&) = delete;
+
+ /**
+ * returns a value from the config. If it doesn’t exist, use
+ * the second argument as the default.
+ */
+ static std::string get(const std::string&, const std::string&);
+ /**
+ * returns a value from the config. If it doesn’t exist, use
+ * the second argument as the default.
+ */
+ static int get_int(const std::string&, const int&);
+ /**
+ * Set a value for the given option. And write all the config
+ * in the file from which it was read if save is true.
+ */
+ static void set(const std::string&, const std::string&, bool save = false);
+ /**
+ * Adds a function to a list. This function will be called whenever a
+ * configuration change occurs (when set() is called, or when the initial
+ * conf is read)
+ */
+ static void connect(const t_config_changed_callback&);
+ /**
+ * Destroy the instance, forcing it to be recreated (with potentially
+ * different parameters) the next time it’s needed.
+ */
+ static void clear();
+ /**
+ * Read the configuration file at the given path.
+ */
+ static bool read_conf(const std::string& name="");
+ /**
+ * Get the filename
+ */
+ static const std::string& get_filename()
+ { return Config::filename; }
+
+private:
+ /**
+ * Set the value of the filename to use, before calling any method.
+ */
+ static std::string filename;
+ /**
+ * Write all the config values into the configuration file
+ */
+ static void save_to_file();
+ /**
+ * Call all the callbacks previously registered using connect().
+ * This is used to notify any class that a configuration change occured.
+ */
+ static void trigger_configuration_change();
+
+ static std::map<std::string, std::string> values;
+ static std::vector<t_config_changed_callback> callbacks;
+
+};
+
+