diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2023-01-11 18:57:18 -0500 |
---|---|---|
committer | John Ericson <John.Ericson@Obsidian.Systems> | 2023-01-11 18:57:18 -0500 |
commit | ce2f91d356438297fd795bd3edb8f9f4536db7da (patch) | |
tree | f5bfc224151eb15fdd25f1e93b367756ff237cab /src/libstore/outputs-spec.cc | |
parent | a7c0cff07f3e1af60bdbcd5bf7e13f8ae768da90 (diff) |
Split `OutputsSpec` and `ExtendedOutputsSpec`, use the former more
`DerivedPath::Built` and `DerivationGoal` were previously using a
regular set with the convention that the empty set means all outputs.
But it is easy to forget about this rule when processing those sets.
Using `OutputSpec` forces us to get it right.
Diffstat (limited to 'src/libstore/outputs-spec.cc')
-rw-r--r-- | src/libstore/outputs-spec.cc | 142 |
1 files changed, 119 insertions, 23 deletions
diff --git a/src/libstore/outputs-spec.cc b/src/libstore/outputs-spec.cc index c446976bc..e7bd8ebd8 100644 --- a/src/libstore/outputs-spec.cc +++ b/src/libstore/outputs-spec.cc @@ -6,57 +6,153 @@ namespace nix { -std::pair<std::string, ExtendedOutputsSpec> ExtendedOutputsSpec::parse(std::string s) +bool OutputsSpec::contains(const std::string & outputName) const { - static std::regex regex(R"((.*)\^((\*)|([a-z]+(,[a-z]+)*)))"); + return std::visit(overloaded { + [&](const OutputsSpec::All &) { + return true; + }, + [&](const OutputsSpec::Names & outputNames) { + return outputNames.count(outputName) > 0; + }, + }, raw()); +} + + +std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s) +{ + static std::regex regex(R"((\*)|([a-z]+(,[a-z]+)*))"); std::smatch match; - if (!std::regex_match(s, match, regex)) - return {s, DefaultOutputs()}; + std::string s2 { s }; // until some improves std::regex + if (!std::regex_match(s2, match, regex)) + return std::nullopt; + + if (match[1].matched) + return { OutputsSpec::All {} }; + + if (match[2].matched) + return { tokenizeString<OutputsSpec::Names>(match[2].str(), ",") }; + + assert(false); +} - if (match[3].matched) - return {match[1], AllOutputs()}; - return {match[1], tokenizeString<OutputNames>(match[4].str(), ",")}; +OutputsSpec OutputsSpec::parse(std::string_view s) +{ + std::optional spec = OutputsSpec::parseOpt(s); + if (!spec) + throw Error("Invalid outputs specifier: '%s'", s); + return *spec; } + +std::pair<std::string_view, ExtendedOutputsSpec> ExtendedOutputsSpec::parse(std::string_view s) +{ + auto found = s.rfind('^'); + + if (found == std::string::npos) + return { s, ExtendedOutputsSpec::Default {} }; + + auto spec = OutputsSpec::parse(s.substr(found + 1)); + return { s.substr(0, found), ExtendedOutputsSpec::Explicit { spec } }; +} + + +std::string OutputsSpec::to_string() const +{ + return std::visit(overloaded { + [&](const OutputsSpec::All &) -> std::string { + return "*"; + }, + [&](const OutputsSpec::Names & outputNames) -> std::string { + return concatStringsSep(",", outputNames); + }, + }, raw()); +} + + std::string ExtendedOutputsSpec::to_string() const { return std::visit(overloaded { [&](const ExtendedOutputsSpec::Default &) -> std::string { return ""; }, - [&](const ExtendedOutputsSpec::All &) -> std::string { - return "*"; + [&](const ExtendedOutputsSpec::Explicit & outputSpec) -> std::string { + return "^" + outputSpec.to_string(); + }, + }, raw()); +} + + +bool OutputsSpec::merge(const OutputsSpec & that) +{ + return std::visit(overloaded { + [&](OutputsSpec::All &) { + /* If we already refer to all outputs, there is nothing to do. */ + return false; }, - [&](const ExtendedOutputsSpec::Names & outputNames) -> std::string { - return "^" + concatStringsSep(",", outputNames); + [&](OutputsSpec::Names & theseNames) { + return std::visit(overloaded { + [&](const OutputsSpec::All &) { + *this = OutputsSpec::All {}; + return true; + }, + [&](const OutputsSpec::Names & thoseNames) { + bool ret = false; + for (auto & i : thoseNames) + if (theseNames.insert(i).second) + ret = true; + return ret; + }, + }, that.raw()); }, }, raw()); } + +void to_json(nlohmann::json & json, const OutputsSpec & outputsSpec) +{ + std::visit(overloaded { + [&](const OutputsSpec::All &) { + json = std::vector<std::string>({"*"}); + }, + [&](const OutputsSpec::Names & names) { + json = names; + }, + }, outputsSpec); +} + + void to_json(nlohmann::json & json, const ExtendedOutputsSpec & extendedOutputsSpec) { - if (std::get_if<DefaultOutputs>(&extendedOutputsSpec)) - json = nullptr; + std::visit(overloaded { + [&](const ExtendedOutputsSpec::Default &) { + json = nullptr; + }, + [&](const ExtendedOutputsSpec::Explicit & e) { + to_json(json, e); + }, + }, extendedOutputsSpec); +} - else if (std::get_if<AllOutputs>(&extendedOutputsSpec)) - json = std::vector<std::string>({"*"}); - else if (auto outputNames = std::get_if<OutputNames>(&extendedOutputsSpec)) - json = *outputNames; +void from_json(const nlohmann::json & json, OutputsSpec & outputsSpec) +{ + auto names = json.get<OutputNames>(); + if (names == OutputNames({"*"})) + outputsSpec = OutputsSpec::All {}; + else + outputsSpec = names; } + void from_json(const nlohmann::json & json, ExtendedOutputsSpec & extendedOutputsSpec) { if (json.is_null()) - extendedOutputsSpec = DefaultOutputs(); + extendedOutputsSpec = ExtendedOutputsSpec::Default {}; else { - auto names = json.get<OutputNames>(); - if (names == OutputNames({"*"})) - extendedOutputsSpec = AllOutputs(); - else - extendedOutputsSpec = names; + extendedOutputsSpec = ExtendedOutputsSpec::Explicit { json.get<OutputsSpec>() }; } } |