aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/deprecated-features.cc
blob: 4de4c8ec7837059a26789df9c8031ba85e15c058 (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
#include "deprecated-features.hh"
// Required for instances of to_json and from_json for DeprecatedFeature
#include "deprecated-features-json.hh"
#include "strings.hh"

#include "nlohmann/json.hpp"

namespace nix {

struct DeprecatedFeatureDetails
{
    DeprecatedFeature tag;
    std::string_view name;
    std::string_view description;
};

/**
 * If two different PRs both add a deprecated feature, and we just
 * used a number for this, we *woudln't* get merge conflict and the
 * counter will be incremented once instead of twice, causing a build
 * failure.
 *
 * By instead defining this instead as 1 + the bottom deprecated
 * feature, we either have no issue at all if few features are not added
 * at the end of the list, or a proper merge conflict if they are.
 */
constexpr size_t numDepFeatures = 1 + static_cast<size_t>(Dep::UrlLiterals);

constexpr std::array<DeprecatedFeatureDetails, numDepFeatures> depFeatureDetails = {{
    {
        .tag = Dep::RecSetOverrides,
        .name = "rec-set-overrides",
        .description = R"(
            Allow `__overrides` in recursive attribute sets.

            Use fix point functions (e.g. `lib.fix` in Nixpkgs) instead.
        )",
    },
    {
        .tag = Dep::AncientLet,
        .name = "ancient-let",
        .description = R"(
            Allow the ancient `let { body = …; … }` syntax.

            Use the `let … in` syntax instead.
        )",
    },
    {
        .tag = Dep::UrlLiterals,
        .name = "url-literals",
        .description = R"(
            Allow unquoted URLs as part of the Nix language syntax.
        )",
    },
}};

static_assert(
    []() constexpr {
        for (auto [index, feature] : enumerate(depFeatureDetails))
            if (index != (size_t)feature.tag)
                return false;
        return true;
    }(),
    "array order does not match enum tag order");

const std::optional<DeprecatedFeature> parseDeprecatedFeature(const std::string_view & name)
{
    using ReverseDepMap = std::map<std::string_view, DeprecatedFeature>;

    static std::unique_ptr<ReverseDepMap> reverseDepMap = []() {
        auto reverseDepMap = std::make_unique<ReverseDepMap>();
        for (auto & depFeature : depFeatureDetails)
            (*reverseDepMap)[depFeature.name] = depFeature.tag;
        return reverseDepMap;
    }();

    if (auto feature = get(*reverseDepMap, name))
        return *feature;
    else
        return std::nullopt;
}

std::string_view showDeprecatedFeature(const DeprecatedFeature tag)
{
    assert((size_t)tag < depFeatureDetails.size());
    return depFeatureDetails[(size_t)tag].name;
}

nlohmann::json documentDeprecatedFeatures()
{
    StringMap res;
    for (auto & depFeature : depFeatureDetails)
        res[std::string { depFeature.name }] =
            trim(stripIndentation(depFeature.description));
    return (nlohmann::json) res;
}

DeprecatedFeatures parseDeprecatedFeatures(const std::set<std::string> & rawFeatures)
{
    DeprecatedFeatures res{};
    for (auto & rawFeature : rawFeatures)
        if (auto feature = parseDeprecatedFeature(rawFeature))
            res = res | *feature;
    return res;
}

MissingDeprecatedFeature::MissingDeprecatedFeature(DeprecatedFeature feature)
    : Error("Lix feature '%1%' is deprecated and should not be used anymore; use '--extra-deprecated-features %1%' to disable this error", showDeprecatedFeature(feature))
    , missingFeature(feature)
{}

std::ostream & operator <<(std::ostream & str, const DeprecatedFeature & feature)
{
    return str << showDeprecatedFeature(feature);
}

void to_json(nlohmann::json & j, const DeprecatedFeature & feature)
{
    j = showDeprecatedFeature(feature);
}

void from_json(const nlohmann::json & j, DeprecatedFeature & feature)
{
    const std::string input = j;
    const auto parsed = parseDeprecatedFeature(input);

    if (parsed.has_value())
        feature = *parsed;
    else
        throw Error("Unknown deprecated feature '%s' in JSON input", input);
}

}