aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/config.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil/config.hh')
-rw-r--r--src/libutil/config.hh157
1 files changed, 126 insertions, 31 deletions
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index 7ac43c854..2675baed7 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -1,12 +1,14 @@
+#pragma once
+///@file
+
#include <cassert>
#include <map>
#include <set>
-#include "types.hh"
-
#include <nlohmann/json_fwd.hpp>
-#pragma once
+#include "types.hh"
+#include "experimental-features.hh"
namespace nix {
@@ -123,21 +125,21 @@ public:
void reapplyUnknownSettings();
};
-/* A class to simplify providing configuration settings. The typical
- use is to inherit Config and add Setting<T> members:
-
- class MyClass : private Config
- {
- Setting<int> foo{this, 123, "foo", "the number of foos to use"};
- Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"};
-
- MyClass() : Config(readConfigFile("/etc/my-app.conf"))
- {
- std::cout << foo << "\n"; // will print 123 unless overridden
- }
- };
-*/
-
+/**
+ * A class to simplify providing configuration settings. The typical
+ * use is to inherit Config and add Setting<T> members:
+ *
+ * class MyClass : private Config
+ * {
+ * Setting<int> foo{this, 123, "foo", "the number of foos to use"};
+ * Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"};
+ *
+ * MyClass() : Config(readConfigFile("/etc/my-app.conf"))
+ * {
+ * std::cout << foo << "\n"; // will print 123 unless overridden
+ * }
+ * };
+ */
class Config : public AbstractConfig
{
friend class AbstractSetting;
@@ -194,12 +196,15 @@ public:
bool overridden = false;
+ std::optional<ExperimentalFeature> experimentalFeature;
+
protected:
AbstractSetting(
const std::string & name,
const std::string & description,
- const std::set<std::string> & aliases);
+ const std::set<std::string> & aliases,
+ std::optional<ExperimentalFeature> experimentalFeature = std::nullopt);
virtual ~AbstractSetting()
{
@@ -210,8 +215,11 @@ protected:
virtual void set(const std::string & value, bool append = false) = 0;
- virtual bool isAppendable()
- { return false; }
+ /**
+ * Whether the type is appendable; i.e. whether the `append`
+ * parameter to `set()` is allowed to be `true`.
+ */
+ virtual bool isAppendable() = 0;
virtual std::string to_string() const = 0;
@@ -224,7 +232,9 @@ protected:
bool isOverridden() const { return overridden; }
};
-/* A setting of type T. */
+/**
+ * A setting of type T.
+ */
template<typename T>
class BaseSetting : public AbstractSetting
{
@@ -234,14 +244,32 @@ protected:
const T defaultValue;
const bool documentDefault;
+ /**
+ * Parse the string into a `T`.
+ *
+ * Used by `set()`.
+ */
+ virtual T parse(const std::string & str) const;
+
+ /**
+ * Append or overwrite `value` with `newValue`.
+ *
+ * Some types to do not support appending in which case `append`
+ * should never be passed. The default handles this case.
+ *
+ * @param append Whether to append or overwrite.
+ */
+ virtual void appendOrSet(T && newValue, bool append);
+
public:
BaseSetting(const T & def,
const bool documentDefault,
const std::string & name,
const std::string & description,
- const std::set<std::string> & aliases = {})
- : AbstractSetting(name, description, aliases)
+ const std::set<std::string> & aliases = {},
+ std::optional<ExperimentalFeature> experimentalFeature = std::nullopt)
+ : AbstractSetting(name, description, aliases, experimentalFeature)
, value(def)
, defaultValue(def)
, documentDefault(documentDefault)
@@ -260,9 +288,25 @@ public:
template<typename U>
void setDefault(const U & v) { if (!overridden) value = v; }
- void set(const std::string & str, bool append = false) override;
+ /**
+ * Require any experimental feature the setting depends on
+ *
+ * Uses `parse()` to get the value from `str`, and `appendOrSet()`
+ * to set it.
+ */
+ void set(const std::string & str, bool append = false) override final;
- bool isAppendable() override;
+ /**
+ * C++ trick; This is template-specialized to compile-time indicate whether
+ * the type is appendable.
+ */
+ struct trait;
+
+ /**
+ * Always defined based on the C++ magic
+ * with `trait` above.
+ */
+ bool isAppendable() override final;
virtual void override(const T & v)
{
@@ -296,8 +340,9 @@ public:
const std::string & name,
const std::string & description,
const std::set<std::string> & aliases = {},
- const bool documentDefault = true)
- : BaseSetting<T>(def, documentDefault, name, description, aliases)
+ const bool documentDefault = true,
+ std::optional<ExperimentalFeature> experimentalFeature = std::nullopt)
+ : BaseSetting<T>(def, documentDefault, name, description, aliases, experimentalFeature)
{
options->addSetting(this);
}
@@ -305,8 +350,10 @@ public:
void operator =(const T & v) { this->assign(v); }
};
-/* A special setting for Paths. These are automatically canonicalised
- (e.g. "/foo//bar/" becomes "/foo/bar"). */
+/**
+ * A special setting for Paths. These are automatically canonicalised
+ * (e.g. "/foo//bar/" becomes "/foo/bar").
+ */
class PathSetting : public BaseSetting<Path>
{
bool allowEmpty;
@@ -325,7 +372,7 @@ public:
options->addSetting(this);
}
- void set(const std::string & str, bool append = false) override;
+ Path parse(const std::string & str) const override;
Path operator +(const char * p) const { return value + p; }
@@ -357,4 +404,52 @@ struct GlobalConfig : public AbstractConfig
extern GlobalConfig globalConfig;
+
+struct ExperimentalFeatureSettings : Config {
+
+ Setting<std::set<ExperimentalFeature>> experimentalFeatures{
+ this, {}, "experimental-features",
+ R"(
+ Experimental features that are enabled.
+
+ Example:
+
+ ```
+ experimental-features = nix-command flakes
+ ```
+
+ The following experimental features are available:
+
+ {{#include experimental-features-shortlist.md}}
+
+ Experimental features are [further documented in the manual](@docroot@/contributing/experimental-features.md).
+ )"};
+
+ /**
+ * Check whether the given experimental feature is enabled.
+ */
+ bool isEnabled(const ExperimentalFeature &) const;
+
+ /**
+ * Require an experimental feature be enabled, throwing an error if it is
+ * not.
+ */
+ void require(const ExperimentalFeature &) const;
+
+ /**
+ * `std::nullopt` pointer means no feature, which means there is nothing that could be
+ * disabled, and so the function returns true in that case.
+ */
+ bool isEnabled(const std::optional<ExperimentalFeature> &) const;
+
+ /**
+ * `std::nullopt` pointer means no feature, which means there is nothing that could be
+ * disabled, and so the function does nothing in that case.
+ */
+ void require(const std::optional<ExperimentalFeature> &) const;
+};
+
+// FIXME: don't use a global variable.
+extern ExperimentalFeatureSettings experimentalFeatureSettings;
+
}