diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 65f48fa2b..11bbdbf84 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -171,7 +171,7 @@ std::vector> InstallableFlake::getCursors(EvalState for (auto & attrPath : attrPaths) { debug("trying flake output attribute '%s'", attrPath); - auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); + auto attr = root->findAlongAttrPath(AttrPath::parse(state, attrPath)); if (attr) { res.push_back(ref(*attr)); } else { diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 2fa2280ea..7e3861e2f 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -355,9 +355,9 @@ void completeFlakeRefWithFragment( attrPathPrefixes.push_back(""); for (auto & attrPathPrefixS : attrPathPrefixes) { - auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS); + auto attrPathPrefix = AttrPath::parse(*evalState, attrPathPrefixS); auto attrPathS = attrPathPrefixS + std::string(fragment); - auto attrPath = parseAttrPath(*evalState, attrPathS); + auto attrPath = AttrPath::parse(*evalState, attrPathS); std::string lastAttr; if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) { @@ -375,9 +375,7 @@ void completeFlakeRefWithFragment( /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); // FIXME: handle names with dots - completions.add( - flakeRefS + "#" + prefixRoot - + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); + completions.add(flakeRefS + "#" + prefixRoot + attrPath2.to_string(*evalState)); } } } @@ -386,7 +384,7 @@ void completeFlakeRefWithFragment( attrpaths. */ if (fragment.empty()) { for (auto & attrPath : defaultFlakeAttrPaths) { - auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); + auto attr = root->findAlongAttrPath(AttrPath::parse(*evalState, attrPath)); if (!attr) continue; completions.add(flakeRefS + "#" + prefixRoot); diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 58705bfa1..575a13542 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -1,5 +1,6 @@ #include "nix/expr/attr-path.hh" #include "nix/expr/eval-inline.hh" +#include "nix/util/strings-inline.hh" namespace nix { @@ -30,14 +31,24 @@ static Strings parseAttrPath(std::string_view s) return res; } -std::vector parseAttrPath(EvalState & state, std::string_view s) +AttrPath AttrPath::parse(EvalState & state, std::string_view s) { - std::vector res; + AttrPath res; for (auto & a : parseAttrPath(s)) res.push_back(state.symbols.create(a)); return res; } +std::string AttrPath::to_string(EvalState & state) const +{ + return dropEmptyInitThenConcatStringsSep(".", state.symbols.resolve({*this})); +} + +std::vector AttrPath::resolve(EvalState & state) const +{ + return state.symbols.resolve({*this}); +} + std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn) { diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 4cfa3dabb..43f10da6e 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -364,7 +364,7 @@ void AttrCursor::fetchCachedValue() throw CachedEvalError(parent->first, parent->second); } -std::vector AttrCursor::getAttrPath() const +AttrPath AttrCursor::getAttrPath() const { if (parent) { auto attrPath = parent->first->getAttrPath(); @@ -374,7 +374,7 @@ std::vector AttrCursor::getAttrPath() const return {}; } -std::vector AttrCursor::getAttrPath(Symbol name) const +AttrPath AttrCursor::getAttrPath(Symbol name) const { auto attrPath = getAttrPath(); attrPath.push_back(name); @@ -383,12 +383,12 @@ std::vector AttrCursor::getAttrPath(Symbol name) const std::string AttrCursor::getAttrPathStr() const { - return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath())); + return getAttrPath().to_string(root->state); } std::string AttrCursor::getAttrPathStr(Symbol name) const { - return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); + return getAttrPath(name).to_string(root->state); } Value & AttrCursor::forceValue() @@ -511,7 +511,7 @@ ref AttrCursor::getAttr(std::string_view name) return getAttr(root->state.symbols.create(name)); } -OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath) +OrSuggestions> AttrCursor::findAlongAttrPath(const AttrPath & attrPath) { auto res = shared_from_this(); for (auto & attr : attrPath) { diff --git a/src/libexpr/include/nix/expr/attr-path.hh b/src/libexpr/include/nix/expr/attr-path.hh index 10e3e300f..fd48705b8 100644 --- a/src/libexpr/include/nix/expr/attr-path.hh +++ b/src/libexpr/include/nix/expr/attr-path.hh @@ -19,6 +19,15 @@ findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & au */ std::pair findPackageFilename(EvalState & state, Value & v, std::string what); -std::vector parseAttrPath(EvalState & state, std::string_view s); +struct AttrPath : std::vector +{ + using std::vector::vector; + + static AttrPath parse(EvalState & state, std::string_view s); + + std::string to_string(EvalState & state) const; + + std::vector resolve(EvalState & state) const; +}; } // namespace nix diff --git a/src/libexpr/include/nix/expr/eval-cache.hh b/src/libexpr/include/nix/expr/eval-cache.hh index 0a0461c19..6d82f8c7e 100644 --- a/src/libexpr/include/nix/expr/eval-cache.hh +++ b/src/libexpr/include/nix/expr/eval-cache.hh @@ -4,6 +4,7 @@ #include "nix/util/sync.hh" #include "nix/util/hash.hh" #include "nix/expr/eval.hh" +#include "nix/expr/attr-path.hh" #include #include @@ -124,9 +125,9 @@ public: Value * value = nullptr, std::optional> && cachedValue = {}); - std::vector getAttrPath() const; + AttrPath getAttrPath() const; - std::vector getAttrPath(Symbol name) const; + AttrPath getAttrPath(Symbol name) const; std::string getAttrPathStr() const; @@ -146,7 +147,7 @@ public: * Get an attribute along a chain of attrsets. Note that this does * not auto-call functors or functions. */ - OrSuggestions> findAlongAttrPath(const std::vector & attrPath); + OrSuggestions> findAlongAttrPath(const AttrPath & attrPath); std::string getString(); diff --git a/src/libexpr/include/nix/expr/symbol-table.hh b/src/libexpr/include/nix/expr/symbol-table.hh index ccee8bee0..f0220376c 100644 --- a/src/libexpr/include/nix/expr/symbol-table.hh +++ b/src/libexpr/include/nix/expr/symbol-table.hh @@ -290,7 +290,7 @@ public: return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first); } - std::vector resolve(const std::vector & symbols) const + std::vector resolve(const std::span & symbols) const { std::vector result; result.reserve(symbols.size()); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e88a5d46c..5324e0121 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -418,7 +418,7 @@ struct CmdFlakeCheck : FlakeCommand return std::nullopt; }; - std::map> attrPathsByDrv; + std::map> attrPathsByDrv; auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { @@ -618,10 +618,7 @@ struct CmdFlakeCheck : FlakeCommand }; // Build and store the attribute path for error reporting - AttrSelectionPath attrPath; - attrPath.push_back(AttrName(state->symbols.create(name))); - attrPath.push_back(AttrName(attr.name)); - attrPath.push_back(AttrName(attr2.name)); + AttrPath attrPath{state->symbols.create(name), attr.name, attr2.name}; attrPathsByDrv[path].push_back(std::move(attrPath)); } } @@ -820,10 +817,9 @@ struct CmdFlakeCheck : FlakeCommand auto it = attrPathsByDrv.find(result.path); if (it != attrPathsByDrv.end() && !it->second.empty()) { for (auto & attrPath : it->second) { - auto attrPathStr = showAttrSelectionPath(state->symbols, attrPath); reportError(Error( "failed to build attribute '%s', build of '%s' failed: %s", - attrPathStr, + attrPath.to_string(*state), result.path.to_string(*store), failure->errorMsg)); } @@ -1172,7 +1168,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto flake = make_ref(lockFlake()); auto localSystem = std::string(settings.thisSystem.get()); - std::function & attrPath, const Symbol & attr)> + std::function hasContent; // For frameworks it's important that structures are as lazy as possible @@ -1181,11 +1177,10 @@ struct CmdFlakeShow : FlakeCommand, MixJSON // to emit more attributes than strictly (sic) necessary. // However, these attributes with empty values are not useful to the user // so we omit them. - hasContent = - [&](eval_cache::AttrCursor & visitor, const std::vector & attrPath, const Symbol & attr) -> bool { + hasContent = [&](eval_cache::AttrCursor & visitor, const AttrPath & attrPath, const Symbol & attr) -> bool { auto attrPath2(attrPath); attrPath2.push_back(attr); - auto attrPathS = state->symbols.resolve(attrPath2); + auto attrPathS = attrPath2.resolve(*state); const auto & attrName = state->symbols[attr]; auto visitor2 = visitor.getAttr(attrName); @@ -1225,20 +1220,20 @@ struct CmdFlakeShow : FlakeCommand, MixJSON std::function & attrPath, + const AttrPath & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; visit = [&](eval_cache::AttrCursor & visitor, - const std::vector & attrPath, + const AttrPath & attrPath, const std::string & headerPrefix, const std::string & nextPrefix) -> nlohmann::json { auto j = nlohmann::json::object(); - auto attrPathS = state->symbols.resolve(attrPath); + auto attrPathS = attrPath.resolve(*state); - Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); + Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", attrPath.to_string(*state))); try { auto recurse = [&]() { @@ -1317,8 +1312,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn( - fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", attrPath.to_string(*state))); } } else { try { @@ -1335,8 +1329,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON headerPrefix)); } else { logger->warn( - fmt("%s omitted due to use of import from derivation", - concatStringsSep(".", attrPathS))); + fmt("%s omitted due to use of import from derivation", attrPath.to_string(*state))); } } } @@ -1354,8 +1347,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON fmt("%s " ANSI_WARNING "omitted due to use of import from derivation" ANSI_NORMAL, headerPrefix)); } else { - logger->warn(fmt( - "%s omitted due to use of import from derivation", concatStringsSep(".", attrPathS))); + logger->warn( + fmt("%s omitted due to use of import from derivation", attrPath.to_string(*state))); } } } @@ -1368,7 +1361,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON logger->cout(fmt( "%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--legacy' to show)", attrPath.to_string(*state))); } } else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { if (!json) @@ -1376,8 +1369,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn( - fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", attrPath.to_string(*state))); } } else { try { @@ -1393,8 +1385,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON headerPrefix)); } else { logger->warn( - fmt("%s omitted due to use of import from derivation", - concatStringsSep(".", attrPathS))); + fmt("%s omitted due to use of import from derivation", attrPath.to_string(*state))); } } } diff --git a/src/nix/search.cc b/src/nix/search.cc index 20bb4cd5d..dac60ceba 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -90,13 +90,13 @@ struct CmdSearch : InstallableValueCommand, MixJSON uint64_t results = 0; - std::function & attrPath, bool initialRecurse)> - visit; + std::function visit; - visit = [&](eval_cache::AttrCursor & cursor, const std::vector & attrPath, bool initialRecurse) { - auto attrPathS = state->symbols.resolve(attrPath); + visit = [&](eval_cache::AttrCursor & cursor, const AttrPath & attrPath, bool initialRecurse) { + auto attrPathS = state->symbols.resolve({attrPath}); + auto attrPathStr = attrPath.to_string(*state); - Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); + Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", attrPathStr)); try { auto recurse = [&]() { for (const auto & attr : cursor.getAttrs()) { @@ -114,7 +114,6 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto aDescription = aMeta ? aMeta->maybeGetAttr(state->s.description) : nullptr; auto description = aDescription ? aDescription->getString() : ""; std::replace(description.begin(), description.end(), '\n', ' '); - auto attrPath2 = concatStringsSep(".", attrPathS); std::vector attrPathMatches; std::vector descriptionMatches; @@ -122,7 +121,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON bool found = false; for (auto & regex : excludeRegexes) { - if (std::regex_search(attrPath2, regex) || std::regex_search(name.name, regex) + if (std::regex_search(attrPathStr, regex) || std::regex_search(name.name, regex) || std::regex_search(description, regex)) return; } @@ -137,7 +136,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON } }; - addAll(std::sregex_iterator(attrPath2.begin(), attrPath2.end(), regex), attrPathMatches); + addAll(std::sregex_iterator(attrPathStr.begin(), attrPathStr.end(), regex), attrPathMatches); addAll(std::sregex_iterator(name.name.begin(), name.name.end(), regex), nameMatches); addAll(std::sregex_iterator(description.begin(), description.end(), regex), descriptionMatches); @@ -148,7 +147,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON if (found) { results++; if (json) { - (*jsonOut)[attrPath2] = { + (*jsonOut)[attrPathStr] = { {"pname", name.name}, {"version", name.version}, {"description", description}, @@ -158,7 +157,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON logger->cout(""); logger->cout( "* %s%s", - wrap("\e[0;1m", hiliteMatches(attrPath2, attrPathMatches, ANSI_GREEN, "\e[0;1m")), + wrap("\e[0;1m", hiliteMatches(attrPathStr, attrPathMatches, ANSI_GREEN, "\e[0;1m")), optionalBracket(" (", name.version, ")")); if (description != "") logger->cout(