aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/config-impl.hh
blob: b9639e761791e97cf8277fbb5c34e538db597ad4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#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<CustomType>` setting, or as derived class of such an
 * instantiation.
 */

#include "config.hh"

namespace nix {

template<> struct BaseSetting<Strings>::trait
{
    static constexpr bool appendable = true;
};
template<> struct BaseSetting<StringSet>::trait
{
    static constexpr bool appendable = true;
};
template<> struct BaseSetting<StringMap>::trait
{
    static constexpr bool appendable = true;
};
template<> struct BaseSetting<std::set<ExperimentalFeature>>::trait
{
    static constexpr bool appendable = true;
};

template<typename T>
struct BaseSetting<T>::trait
{
    static constexpr bool appendable = false;
};

template<typename T>
bool BaseSetting<T>::isAppendable()
{
    return trait::appendable;
}

template<> void BaseSetting<Strings>::appendOrSet(Strings && newValue, bool append);
template<> void BaseSetting<StringSet>::appendOrSet(StringSet && newValue, bool append);
template<> void BaseSetting<StringMap>::appendOrSet(StringMap && newValue, bool append);
template<> void BaseSetting<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> && newValue, bool append);

template<typename T>
void BaseSetting<T>::appendOrSet(T && newValue, bool append)
{
    static_assert(
        !trait::appendable,
        "using default `appendOrSet` implementation with an appendable type");
    assert(!append);

    value = std::move(newValue);
}

template<typename T>
void BaseSetting<T>::set(const std::string & str, bool append)
{
    if (experimentalFeatureSettings.isEnabled(experimentalFeature))
        appendOrSet(parse(str), append);
    else {
        assert(experimentalFeature);
        warn("Ignoring setting '%s' because experimental feature '%s' is not enabled",
            name,
            showExperimentalFeature(*experimentalFeature));
    }
}

template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & category);

template<typename T>
void BaseSetting<T>::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<std::string>)
DECLARE_CONFIG_SERIALISER(bool)
DECLARE_CONFIG_SERIALISER(Strings)
DECLARE_CONFIG_SERIALISER(StringSet)
DECLARE_CONFIG_SERIALISER(StringMap)
DECLARE_CONFIG_SERIALISER(std::set<ExperimentalFeature>)

template<typename T>
T BaseSetting<T>::parse(const std::string & str) const
{
    static_assert(std::is_integral<T>::value, "Integer required.");

    if (auto n = string2Int<T>(str))
        return *n;
    else
        throw UsageError("setting '%s' has invalid value '%s'", name, str);
}

template<typename T>
std::string BaseSetting<T>::to_string() const
{
    static_assert(std::is_integral<T>::value, "Integer required.");

    return std::to_string(value);
}

}