diff options
author | regnat <rg@regnat.ovh> | 2022-03-04 09:44:00 +0100 |
---|---|---|
committer | regnat <rg@regnat.ovh> | 2022-03-07 10:09:10 +0100 |
commit | 98e361ad4c1a26d4ffe4762a6f33bb9e39321a39 (patch) | |
tree | 44d0ab1781f96511d4c95c274f6b64f943a070b0 | |
parent | 2405bbbb5edd204d6031edcd470a2057f4a25782 (diff) |
Also display suggestions for the commands using the eval cache
Make `nix build .#nix-armv8l-linux` work for example
-rw-r--r-- | src/libcmd/installables.cc | 19 | ||||
-rw-r--r-- | src/libexpr/eval-cache.cc | 35 | ||||
-rw-r--r-- | src/libexpr/eval-cache.hh | 8 | ||||
-rw-r--r-- | src/libutil/suggestions.hh | 58 |
4 files changed, 103 insertions, 17 deletions
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 888d863ff..03f3bd409 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -272,9 +272,9 @@ void completeFlakeRefWithFragment( auto attr = root->findAlongAttrPath(attrPath); if (!attr) continue; - for (auto & attr2 : attr->getAttrs()) { + for (auto & attr2 : (*attr)->getAttrs()) { if (hasPrefix(attr2, lastAttr)) { - auto attrPath2 = attr->getAttrPath(attr2); + auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); completions->add(flakeRefS + "#" + concatStringsSep(".", attrPath2)); @@ -568,15 +568,22 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF auto cache = openEvalCache(*state, lockedFlake); auto root = cache->getRoot(); + Suggestions suggestions; + for (auto & attrPath : getActualAttrPaths()) { debug("trying flake output attribute '%s'", attrPath); - auto attr = root->findAlongAttrPath( + auto attrOrSuggestions = root->findAlongAttrPath( parseAttrPath(*state, attrPath), true ); - if (!attr) continue; + if (!attrOrSuggestions) { + suggestions += attrOrSuggestions.getSuggestions(); + continue; + } + + auto attr = *attrOrSuggestions; if (!attr->isDerivation()) throw Error("flake output attribute '%s' is not a derivation", attrPath); @@ -591,7 +598,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)}; } - throw Error("flake '%s' does not provide attribute %s", + throw Error(suggestions, "flake '%s' does not provide attribute %s", flakeRef, showAttrPaths(getActualAttrPaths())); } @@ -642,7 +649,7 @@ InstallableFlake::getCursors(EvalState & state) for (auto & attrPath : getActualAttrPaths()) { auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); - if (attr) res.push_back({attr, attrPath}); + if (attr) res.push_back({*attr, attrPath}); } return res; diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 00d0749f9..188223957 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -406,6 +406,16 @@ Value & AttrCursor::forceValue() return v; } +Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) +{ + auto attrNames = getAttrs(); + std::set<std::string> strAttrNames; + for (auto & name : attrNames) + strAttrNames.insert(std::string(name)); + + return Suggestions::bestMatches(strAttrNames, name); +} + std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) { if (root->db) { @@ -446,6 +456,11 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro return nullptr; //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); + for (auto & attr : *v.attrs) { + if (root->db) + root->db->setPlaceholder({cachedValue->first, attr.name}); + } + auto attr = v.attrs->get(name); if (!attr) { @@ -464,7 +479,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), placeholder_t()}; } - return std::make_shared<AttrCursor>( + return make_ref<AttrCursor>( root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); } @@ -473,27 +488,31 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name) return maybeGetAttr(root->state.symbols.create(name)); } -std::shared_ptr<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors) +ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors) { auto p = maybeGetAttr(name, forceErrors); if (!p) throw Error("attribute '%s' does not exist", getAttrPathStr(name)); - return p; + return ref(p); } -std::shared_ptr<AttrCursor> AttrCursor::getAttr(std::string_view name) +ref<AttrCursor> AttrCursor::getAttr(std::string_view name) { return getAttr(root->state.symbols.create(name)); } -std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force) +OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force) { auto res = shared_from_this(); for (auto & attr : attrPath) { - res = res->maybeGetAttr(attr, force); - if (!res) return {}; + auto child = res->maybeGetAttr(attr, force); + if (!child) { + auto suggestions = res->getSuggestionsForAttr(attr); + return OrSuggestions<ref<AttrCursor>>::failed(suggestions); + } + res = child; } - return res; + return ref(res); } std::string AttrCursor::getString() diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 43b34ebcb..40f1d4ffc 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -94,15 +94,17 @@ public: std::string getAttrPathStr(Symbol name) const; + Suggestions getSuggestionsForAttr(Symbol name); + std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false); std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name); - std::shared_ptr<AttrCursor> getAttr(Symbol name, bool forceErrors = false); + ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false); - std::shared_ptr<AttrCursor> getAttr(std::string_view name); + ref<AttrCursor> getAttr(std::string_view name); - std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false); + OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false); std::string getString(); diff --git a/src/libutil/suggestions.hh b/src/libutil/suggestions.hh index 76063a261..63426259d 100644 --- a/src/libutil/suggestions.hh +++ b/src/libutil/suggestions.hh @@ -40,4 +40,62 @@ public: Suggestions& operator+=(const Suggestions & other); }; +// Either a value of type `T`, or some suggestions +template<typename T> +class OrSuggestions { +public: + using Raw = std::variant<T, Suggestions>; + + Raw raw; + + T* operator ->() + { + return &**this; + } + + T& operator *() + { + if (auto elt = std::get_if<T>(&raw)) + return *elt; + throw Error("Invalid access to a failed value"); + } + + operator bool() const noexcept + { + return std::holds_alternative<T>(raw); + } + + OrSuggestions(T t) + : raw(t) + { + } + + OrSuggestions() + : raw(Suggestions{}) + { + } + + static OrSuggestions<T> failed(const Suggestions & s) + { + auto res = OrSuggestions<T>(); + res.raw = s; + return res; + } + + static OrSuggestions<T> failed() + { + return OrSuggestions<T>::failed(Suggestions{}); + } + + const Suggestions & get_suggestions() + { + static Suggestions noSuggestions; + if (const auto & suggestions = std::get_if<Suggestions>(&raw)) + return *suggestions; + else + return noSuggestions; + } + +}; + } |