#pragma once /** * @file * * Template implementations (as opposed to mere declarations). * * This file is an exmample of the "impl.hh" pattern. See the * contributing guide. * * One only needs to include this when one is declaring a * `BaseClass` setting, or as derived class of such an * instantiation. */ #include "args.hh" #include "config.hh" #include "logging.hh" namespace nix { template<> struct BaseSetting::trait { static constexpr bool appendable = true; }; template<> struct BaseSetting::trait { static constexpr bool appendable = true; }; template<> struct BaseSetting::trait { static constexpr bool appendable = true; }; template<> struct BaseSetting::trait { static constexpr bool appendable = true; }; template<> struct BaseSetting::trait { static constexpr bool appendable = true; }; template struct BaseSetting::trait { static constexpr bool appendable = false; }; template bool BaseSetting::isAppendable() { return trait::appendable; } template<> void BaseSetting::appendOrSet(Strings newValue, bool append); template<> void BaseSetting::appendOrSet(StringSet newValue, bool append); template<> void BaseSetting::appendOrSet(StringMap newValue, bool append); template<> void BaseSetting::appendOrSet(ExperimentalFeatures newValue, bool append); template<> void BaseSetting::appendOrSet(DeprecatedFeatures newValue, bool append); template void BaseSetting::appendOrSet(T newValue, bool append) { static_assert( !trait::appendable, "using default `appendOrSet` implementation with an appendable type"); assert(!append); value = std::move(newValue); } template void BaseSetting::set(const std::string & str, bool append) { if (experimentalFeatureSettings.isEnabled(experimentalFeature)) { auto parsed = parse(str); if (deprecated && (append || parsed != value)) { warn("deprecated setting '%s' found (set to '%s')", name, str); } appendOrSet(std::move(parsed), append); } else { assert(experimentalFeature); warn("Ignoring setting '%s' because experimental feature '%s' is not enabled", name, showExperimentalFeature(*experimentalFeature)); } } template<> void BaseSetting::convertToArg(Args & args, const std::string & category); template void BaseSetting::convertToArg(Args & args, const std::string & category) { args.addFlag({ .longName = name, .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, .handler = {[this](std::string s) { overridden = true; set(s); }}, .experimentalFeature = experimentalFeature, }); if (isAppendable()) args.addFlag({ .longName = "extra-" + name, .description = fmt("Append to the `%s` setting.", name), .category = category, .labels = {"value"}, .handler = {[this](std::string s) { overridden = true; set(s, true); }}, .experimentalFeature = experimentalFeature, }); } #define DECLARE_CONFIG_SERIALISER(TY) \ template<> TY BaseSetting< TY >::parse(const std::string & str) const; \ template<> std::string BaseSetting< TY >::to_string() const; DECLARE_CONFIG_SERIALISER(std::string) DECLARE_CONFIG_SERIALISER(std::optional) DECLARE_CONFIG_SERIALISER(bool) DECLARE_CONFIG_SERIALISER(Strings) DECLARE_CONFIG_SERIALISER(StringSet) DECLARE_CONFIG_SERIALISER(StringMap) DECLARE_CONFIG_SERIALISER(ExperimentalFeatures) DECLARE_CONFIG_SERIALISER(DeprecatedFeatures) template T BaseSetting::parse(const std::string & str) const { static_assert(std::is_integral::value, "Integer required."); if (auto n = string2Int(str)) return *n; else throw UsageError("setting '%s' has invalid value '%s'", name, str); } template std::string BaseSetting::to_string() const { static_assert(std::is_integral::value, "Integer required."); return std::to_string(value); } }