#include #include #include "types.hh" #pragma once namespace nix { class Args; class AbstractSetting; class JSONPlaceholder; class JSONObject; /* A class to simplify providing configuration settings. The typical use is to inherit Config and add Setting members: class MyClass : private Config { Setting foo{this, 123, "foo", "the number of foos to use"}; Setting bar{this, "blabla", "bar", "the name of the bar"}; MyClass() : Config(readConfigFile("/etc/my-app.conf")) { std::cout << foo << "\n"; // will print 123 unless overriden } }; */ class Config { friend class AbstractSetting; public: struct SettingData { bool isAlias; AbstractSetting * setting; SettingData(bool isAlias, AbstractSetting * setting) : isAlias(isAlias), setting(setting) { } }; typedef std::map Settings; private: Settings _settings; StringMap initials; public: Config(const StringMap & initials) : initials(initials) { } void set(const std::string & name, const std::string & value); void addSetting(AbstractSetting * setting); void warnUnknownSettings(); StringMap getSettings(bool overridenOnly = false); const Settings & _getSettings() { return _settings; } void applyConfigFile(const Path & path, bool fatal = false); void resetOverriden(); void toJSON(JSONObject & out); void convertToArgs(Args & args, const std::string & category); }; class AbstractSetting { friend class Config; public: const std::string name; const std::string description; const std::set aliases; int created = 123; bool overriden = false; protected: AbstractSetting( const std::string & name, const std::string & description, const std::set & aliases); virtual ~AbstractSetting() { // Check against a gcc miscompilation causing our constructor // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431). assert(created == 123); } virtual void set(const std::string & value) = 0; virtual std::string to_string() = 0; virtual void toJSON(JSONPlaceholder & out); virtual void convertToArg(Args & args, const std::string & category); bool isOverriden() { return overriden; } }; /* A setting of type T. */ template class BaseSetting : public AbstractSetting { protected: T value; public: BaseSetting(const T & def, const std::string & name, const std::string & description, const std::set & aliases = {}) : AbstractSetting(name, description, aliases) , value(def) { } operator const T &() const { return value; } operator T &() { return value; } const T & get() const { return value; } bool operator ==(const T & v2) const { return value == v2; } bool operator !=(const T & v2) const { return value != v2; } void operator =(const T & v) { assign(v); } virtual void assign(const T & v) { value = v; } void set(const std::string & str) override; std::string to_string() override; void convertToArg(Args & args, const std::string & category) override; void toJSON(JSONPlaceholder & out) override; }; template std::ostream & operator <<(std::ostream & str, const BaseSetting & opt) { str << (const T &) opt; return str; } template bool operator ==(const T & v1, const BaseSetting & v2) { return v1 == (const T &) v2; } template class Setting : public BaseSetting { public: Setting(Config * options, const T & def, const std::string & name, const std::string & description, const std::set & aliases = {}) : BaseSetting(def, name, description, aliases) { options->addSetting(this); } void operator =(const T & v) { this->assign(v); } }; /* A special setting for Paths. These are automatically canonicalised (e.g. "/foo//bar/" becomes "/foo/bar"). */ class PathSetting : public BaseSetting { bool allowEmpty; public: PathSetting(Config * options, bool allowEmpty, const Path & def, const std::string & name, const std::string & description, const std::set & aliases = {}) : BaseSetting(def, name, description, aliases) , allowEmpty(allowEmpty) { options->addSetting(this); } void set(const std::string & str) override; Path operator +(const char * p) const { return value + p; } void operator =(const Path & v) { this->assign(v); } }; }