diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2017-04-13T18·53+0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2017-04-13T18·53+0200 |
commit | ba9ad29fdbfda3836bb06b35817f08fd10beaa22 (patch) | |
tree | 565646143793af4e91ee88630e667bb7976e8686 /src/libutil | |
parent | 6bd9576aeb55927cb551736a47b4e8e3fd1063bb (diff) |
Convert Settings to the new config system
This makes all config options self-documenting. Unknown or unparseable config settings and --option flags now cause a warning.
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/config.cc | 104 | ||||
-rw-r--r-- | src/libutil/config.hh | 22 |
2 files changed, 100 insertions, 26 deletions
diff --git a/src/libutil/config.cc b/src/libutil/config.cc index c05a3253bce6..85e5ce330bee 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -11,7 +11,7 @@ void Config::set(const std::string & name, const std::string & value) i->second.setting->set(value); } -void Config::add(AbstractSetting * setting) +void Config::addSetting(AbstractSetting * setting) { _settings.emplace(setting->name, Config::SettingData{false, setting}); for (auto & alias : setting->aliases) @@ -41,21 +41,59 @@ void Config::add(AbstractSetting * setting) } } -void Config::warnUnused() +void Config::warnUnknownSettings() { for (auto & i : initials) warn("unknown setting '%s'", i.first); } -std::string Config::dump() +StringMap Config::getSettings() { - std::string res; + StringMap res; for (auto & opt : _settings) if (!opt.second.isAlias) - res += opt.first + " = " + opt.second.setting->to_string() + "\n"; + res.emplace(opt.first, opt.second.setting->to_string()); return res; } +void Config::applyConfigFile(const Path & path, bool fatal) +{ + try { + string contents = readFile(path); + + unsigned int pos = 0; + + while (pos < contents.size()) { + string line; + while (pos < contents.size() && contents[pos] != '\n') + line += contents[pos++]; + pos++; + + string::size_type hash = line.find('#'); + if (hash != string::npos) + line = string(line, 0, hash); + + vector<string> tokens = tokenizeString<vector<string> >(line); + if (tokens.empty()) continue; + + if (tokens.size() < 2 || tokens[1] != "=") + throw UsageError("illegal configuration line ‘%1%’ in ‘%2%’", line, path); + + string name = tokens[0]; + + vector<string>::iterator i = tokens.begin(); + advance(i, 2); + + try { + set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow + } catch (UsageError & e) { + if (fatal) throw; + warn("in configuration file '%s': %s", path, e.what()); + } + }; + } catch (SysError &) { } +} + AbstractSetting::AbstractSetting( const std::string & name, const std::string & description, @@ -74,41 +112,65 @@ template<> std::string Setting<std::string>::to_string() return value; } -template<typename T> -void Setting<T>::set(const std::string & str) +template<typename T, typename Tag> +void Setting<T, Tag>::set(const std::string & str) { static_assert(std::is_integral<T>::value, "Integer required."); - try { - auto i = std::stoll(str); - if (i < std::numeric_limits<T>::min() || - i > std::numeric_limits<T>::max()) - throw UsageError("setting '%s' has out-of-range value %d", name, i); - value = i; - } catch (std::logic_error&) { + if (!string2Int(str, value)) throw UsageError("setting '%s' has invalid value '%s'", name, str); - } } -template<typename T> -std::string Setting<T>::to_string() +template<typename T, typename Tag> +std::string Setting<T, Tag>::to_string() { static_assert(std::is_integral<T>::value, "Integer required."); return std::to_string(value); } -template<> void Setting<bool>::set(const std::string & str) +bool AbstractSetting::parseBool(const std::string & str) { if (str == "true" || str == "yes" || str == "1") - value = true; + return true; else if (str == "false" || str == "no" || str == "0") - value = false; + return false; else throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); } +template<> void Setting<bool>::set(const std::string & str) +{ + value = parseBool(str); +} + +std::string AbstractSetting::printBool(bool b) +{ + return b ? "true" : "false"; +} + + template<> std::string Setting<bool>::to_string() { - return value ? "true" : "false"; + return printBool(value); +} + +template<> void Setting<Strings>::set(const std::string & str) +{ + value = tokenizeString<Strings>(str); +} + +template<> std::string Setting<Strings>::to_string() +{ + return concatStringsSep(" ", value); +} + +template<> void Setting<StringSet>::set(const std::string & str) +{ + value = tokenizeString<StringSet>(str); +} + +template<> std::string Setting<StringSet>::to_string() +{ + return concatStringsSep(" ", value); } template class Setting<int>; diff --git a/src/libutil/config.hh b/src/libutil/config.hh index fb2d48e9c834..6c8612f675c7 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -47,11 +47,13 @@ public: void set(const std::string & name, const std::string & value); - void add(AbstractSetting * setting); + void addSetting(AbstractSetting * setting); - void warnUnused(); + void warnUnknownSettings(); - std::string dump(); + StringMap getSettings(); + + void applyConfigFile(const Path & path, bool fatal = false); }; class AbstractSetting @@ -83,10 +85,15 @@ protected: virtual void set(const std::string & value) = 0; virtual std::string to_string() = 0; + + bool parseBool(const std::string & str); + std::string printBool(bool b); }; +struct DefaultSettingTag { }; + /* A setting of type T. */ -template<typename T> +template<typename T, typename Tag = DefaultSettingTag> class Setting : public AbstractSetting { protected: @@ -103,10 +110,12 @@ public: : AbstractSetting(name, description, aliases) , value(def) { - options->add(this); + options->addSetting(this); } 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) { value = v; } @@ -123,6 +132,9 @@ std::ostream & operator <<(std::ostream & str, const Setting<T> & opt) return str; } +template<typename T> +bool operator ==(const T & v1, const Setting<T> & v2) { return v1 == (const T &) v2; } + /* A special setting for Paths. These are automatically canonicalised (e.g. "/foo//bar/" becomes "/foo/bar"). */ class PathSetting : public Setting<Path> |