aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/fetchers/indirect.cc
blob: 963abd85f0f9da7a8267b44e33f97e437bb6120d (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
134
135
136
137
138
139
140
141
142
#include "fetchers.hh"
#include "parse.hh"
#include "regex.hh"

namespace nix::fetchers {

std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript);

struct IndirectInput : Input
{
    std::string id;
    std::optional<Hash> rev;
    std::optional<std::string> ref;

    std::string type() const override { return "indirect"; }

    bool operator ==(const Input & other) const override
    {
        auto other2 = dynamic_cast<const IndirectInput *>(&other);
        return
            other2
            && id == other2->id
            && rev == other2->rev
            && ref == other2->ref;
    }

    bool isDirect() const override
    {
        return false;
    }

    std::optional<std::string> getRef() const override { return ref; }

    std::optional<Hash> getRev() const override { return rev; }

    bool contains(const Input & other) const override
    {
        auto other2 = dynamic_cast<const IndirectInput *>(&other);
        return
            other2
            && id == other2->id
            && (!ref || ref == other2->ref)
            && (!rev || rev == other2->rev);
    }

    std::string to_string() const override
    {
        ParsedURL url;
        url.scheme = "flake";
        url.path = id;
        if (ref) { url.path += '/'; url.path += *ref; };
        if (rev) { url.path += '/'; url.path += rev->gitRev(); };
        return url.to_string();
    }

    Attrs toAttrsInternal() const override
    {
        Attrs attrs;
        attrs.emplace("id", id);
        if (ref)
            attrs.emplace("ref", *ref);
        if (rev)
            attrs.emplace("rev", rev->gitRev());
        return attrs;
    }

    std::shared_ptr<const Input> applyOverrides(
        std::optional<std::string> ref,
        std::optional<Hash> rev) const override
    {
        if (!ref && !rev) return shared_from_this();

        auto res = std::make_shared<IndirectInput>(*this);

        if (ref) res->ref = ref;
        if (rev) res->rev = rev;

        return res;
    }

    std::pair<Tree, std::shared_ptr<const Input>> fetchTreeInternal(nix::ref<Store> store) const override
    {
        throw Error("indirect input '%s' cannot be fetched directly", to_string());
    }
};

struct IndirectInputScheme : InputScheme
{
    std::unique_ptr<Input> inputFromURL(const ParsedURL & url) override
    {
        if (url.scheme != "flake") return nullptr;

        auto path = tokenizeString<std::vector<std::string>>(url.path, "/");
        auto input = std::make_unique<IndirectInput>();

        if (path.size() == 1) {
        } else if (path.size() == 2) {
            if (std::regex_match(path[1], revRegex))
                input->rev = Hash(path[1], htSHA1);
            else if (std::regex_match(path[1], refRegex))
                input->ref = path[1];
            else
                throw BadURL("in flake URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[1]);
        } else if (path.size() == 3) {
            if (!std::regex_match(path[1], refRegex))
                throw BadURL("in flake URL '%s', '%s' is not a branch/tag name", url.url, path[1]);
            input->ref = path[1];
            if (!std::regex_match(path[2], revRegex))
                throw BadURL("in flake URL '%s', '%s' is not a commit hash", url.url, path[2]);
            input->rev = Hash(path[2], htSHA1);
        } else
            throw BadURL("GitHub URL '%s' is invalid", url.url);

        // FIXME: forbid query params?

        input->id = path[0];
        if (!std::regex_match(input->id, flakeRegex))
            throw BadURL("'%s' is not a valid flake ID", input->id);

        return input;
    }

    std::unique_ptr<Input> inputFromAttrs(const Attrs & attrs) override
    {
        if (maybeGetStrAttr(attrs, "type") != "indirect") return {};

        for (auto & [name, value] : attrs)
            if (name != "type" && name != "id" && name != "ref" && name != "rev")
                throw Error("unsupported indirect input attribute '%s'", name);

        auto input = std::make_unique<IndirectInput>();
        input->id = getStrAttr(attrs, "id");
        input->ref = maybeGetStrAttr(attrs, "ref");
        if (auto rev = maybeGetStrAttr(attrs, "rev"))
            input->rev = Hash(*rev, htSHA1);
        return input;
    }
};

static auto r1 = OnStartup([] { registerInputScheme(std::make_unique<IndirectInputScheme>()); });

}