summaryrefslogtreecommitdiff
path: root/src/buildtool/common/action_description.hpp
blob: f830eb825066c358141af7b56131504fa5563970 (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#ifndef INCLUDED_SRC_BUILDTOOL_COMMON_ACTION_DESCRIPTION_HPP
#define INCLUDED_SRC_BUILDTOOL_COMMON_ACTION_DESCRIPTION_HPP

#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

#include "nlohmann/json.hpp"
#include "src/buildtool/common/action.hpp"
#include "src/buildtool/common/artifact_description.hpp"

class ActionDescription {
  public:
    using outputs_t = std::vector<std::string>;
    using inputs_t = std::unordered_map<std::string, ArtifactDescription>;
    using Ptr = std::shared_ptr<ActionDescription>;

    ActionDescription(outputs_t output_files,
                      outputs_t output_dirs,
                      Action action,
                      inputs_t inputs)
        : output_files_{std::move(output_files)},
          output_dirs_{std::move(output_dirs)},
          action_{std::move(action)},
          inputs_{std::move(inputs)} {}

    [[nodiscard]] static auto FromJson(std::string const& id,
                                       nlohmann::json const& desc) noexcept
        -> std::optional<ActionDescription::Ptr> {
        try {
            auto outputs =
                ExtractValueAs<std::vector<std::string>>(desc, "output");
            auto output_dirs =
                ExtractValueAs<std::vector<std::string>>(desc, "output_dirs");
            auto command =
                ExtractValueAs<std::vector<std::string>>(desc, "command");

            if ((not outputs.has_value() or outputs->empty()) and
                (not output_dirs.has_value() or output_dirs->empty())) {
                Logger::Log(
                    LogLevel::Error,
                    "Action description for action \"{}\" incomplete: values "
                    "for either \"output\" or \"output_dir\" must be non-empty "
                    "array.",
                    id);
                return std::nullopt;
            }

            if (not command.has_value() or command->empty()) {
                Logger::Log(
                    LogLevel::Error,
                    "Action description for action \"{}\" incomplete: values "
                    "for \"command\" must be non-empty array.",
                    id);
                return std::nullopt;
            }

            if (not outputs) {
                outputs = std::vector<std::string>{};
            }
            if (not output_dirs) {
                output_dirs = std::vector<std::string>{};
            }

            auto optional_key_value_reader =
                [](nlohmann::json const& action_desc,
                   std::string const& key) -> nlohmann::json {
                auto it = action_desc.find(key);
                if (it == action_desc.end()) {
                    return nlohmann::json::object();
                }
                return *it;
            };
            auto const input = optional_key_value_reader(desc, "input");
            auto const env = optional_key_value_reader(desc, "env");

            if (not(input.is_object() and env.is_object())) {
                Logger::Log(
                    LogLevel::Error,
                    "Action description for action \"{}\" type error: values "
                    "for \"input\" and \"env\" must be objects.",
                    id);
                return std::nullopt;
            }

            inputs_t inputs{};
            for (auto const& [path, input_desc] : input.items()) {
                auto artifact = ArtifactDescription::FromJson(input_desc);
                if (not artifact) {
                    return std::nullopt;
                }
                inputs.emplace(path, std::move(*artifact));
            }
            std::optional<std::string> may_fail{};
            bool no_cache{};
            auto may_fail_it = desc.find("may_fail");
            if (may_fail_it != desc.end()) {
                if (not may_fail_it->is_string()) {
                    Logger::Log(LogLevel::Error,
                                "may_fail has to be a boolean");
                    return std::nullopt;
                }
                may_fail = *may_fail_it;
            }
            auto no_cache_it = desc.find("no_cache");
            if (no_cache_it != desc.end()) {
                if (not no_cache_it->is_boolean()) {
                    Logger::Log(LogLevel::Error,
                                "no_cache has to be a boolean");
                    return std::nullopt;
                }
                no_cache = *no_cache_it;
            }

            return std::make_shared<ActionDescription>(
                std::move(*outputs),
                std::move(*output_dirs),
                Action{id, std::move(*command), env, may_fail, no_cache},
                inputs);
        } catch (std::exception const& ex) {
            Logger::Log(
                LogLevel::Error,
                "Failed to parse action description from JSON with error:\n{}",
                ex.what());
        }
        return std::nullopt;
    }

    [[nodiscard]] auto Id() const noexcept -> ActionIdentifier {
        return action_.Id();
    }

    [[nodiscard]] auto ToJson() const noexcept -> nlohmann::json {
        auto json = nlohmann::json{{"command", action_.Command()}};
        if (not output_files_.empty()) {
            json["output"] = output_files_;
        }
        if (not output_dirs_.empty()) {
            json["output_dirs"] = output_dirs_;
        }
        if (not inputs_.empty()) {
            auto inputs = nlohmann::json::object();
            for (auto const& [path, artifact] : inputs_) {
                inputs[path] = artifact.ToJson();
            }
            json["input"] = inputs;
        }
        if (not action_.Env().empty()) {
            json["env"] = action_.Env();
        }
        if (action_.MayFail()) {
            json["may_fail"] = *action_.MayFail();
        }
        if (action_.NoCache()) {
            json["no_cache"] = true;
        }
        return json;
    }

    [[nodiscard]] auto OutputFiles() const& noexcept -> outputs_t const& {
        return output_files_;
    }

    [[nodiscard]] auto OutputFiles() && noexcept -> outputs_t {
        return std::move(output_files_);
    }

    [[nodiscard]] auto OutputDirs() const& noexcept -> outputs_t const& {
        return output_dirs_;
    }

    [[nodiscard]] auto OutputDirs() && noexcept -> outputs_t {
        return std::move(output_dirs_);
    }

    [[nodiscard]] auto GraphAction() const& noexcept -> Action const& {
        return action_;
    }

    [[nodiscard]] auto GraphAction() && noexcept -> Action {
        return std::move(action_);
    }

    [[nodiscard]] auto Inputs() const& noexcept -> inputs_t const& {
        return inputs_;
    }

    [[nodiscard]] auto Inputs() && noexcept -> inputs_t {
        return std::move(inputs_);
    }

  private:
    outputs_t output_files_;
    outputs_t output_dirs_;
    Action action_;
    inputs_t inputs_;
};

#endif  // INCLUDED_SRC_BUILDTOOL_COMMON_ACTION_DESCRIPTION_HPP