aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/outputs-spec.cc
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2023-01-11 18:57:18 -0500
committerJohn Ericson <John.Ericson@Obsidian.Systems>2023-01-11 18:57:18 -0500
commitce2f91d356438297fd795bd3edb8f9f4536db7da (patch)
treef5bfc224151eb15fdd25f1e93b367756ff237cab /src/libstore/outputs-spec.cc
parenta7c0cff07f3e1af60bdbcd5bf7e13f8ae768da90 (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.cc142
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>() };
}
}