libexpr: standardize on strings for attr cache traversal

it's all strings anyway. the db stores strings, the cli wants to
interact with attr paths as strings, so we will just use strings

Change-Id: Id9ea07d92343de77e8d47af8fec1e86ae225e9a1
This commit is contained in:
eldritch horrors 2024-12-03 20:38:41 +01:00
parent dd7d7450a5
commit 9a3e3a5560
12 changed files with 155 additions and 207 deletions

View file

@ -118,8 +118,8 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
std::optional<NixInt::Inner> priority; std::optional<NixInt::Inner> priority;
if (attr->maybeGetAttr(state->s.outputSpecified)) { if (attr->maybeGetAttr("outputSpecified")) {
} else if (auto aMeta = attr->maybeGetAttr(state->s.meta)) { } else if (auto aMeta = attr->maybeGetAttr("meta")) {
if (auto aPriority = aMeta->maybeGetAttr("priority")) if (auto aPriority = aMeta->maybeGetAttr("priority"))
priority = aPriority->getInt().value; priority = aPriority->getInt().value;
} }
@ -130,12 +130,12 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
.outputs = std::visit(overloaded { .outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec { [&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall; std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr(state->s.outputSpecified)) { if (auto aOutputSpecified = attr->maybeGetAttr("outputSpecified")) {
if (aOutputSpecified->getBool()) { if (aOutputSpecified->getBool()) {
if (auto aOutputName = attr->maybeGetAttr("outputName")) if (auto aOutputName = attr->maybeGetAttr("outputName"))
outputsToInstall = { aOutputName->getString() }; outputsToInstall = { aOutputName->getString() };
} }
} else if (auto aMeta = attr->maybeGetAttr(state->s.meta)) { } else if (auto aMeta = attr->maybeGetAttr("meta")) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall")) if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings()) for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s); outputsToInstall.insert(s);
@ -184,7 +184,7 @@ InstallableFlake::getCursors()
for (auto & attrPath : attrPaths) { for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath); debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); auto attr = root->findAlongAttrPath(parseAttrPath(attrPath));
if (attr) { if (attr) {
res.push_back(ref(*attr)); res.push_back(ref(*attr));
} else { } else {

View file

@ -305,13 +305,13 @@ void completeFlakeRefWithFragment(
attrPathPrefixes.push_back(""); attrPathPrefixes.push_back("");
for (auto & attrPathPrefixS : attrPathPrefixes) { for (auto & attrPathPrefixS : attrPathPrefixes) {
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS); auto attrPathPrefix = parseAttrPath(attrPathPrefixS);
auto attrPathS = attrPathPrefixS + std::string(fragment); auto attrPathS = attrPathPrefixS + std::string(fragment);
auto attrPath = parseAttrPath(*evalState, attrPathS); auto attrPath = parseAttrPath(attrPathS);
std::string lastAttr; std::string lastAttr;
if (!attrPath.empty() && !attrPathS.ends_with(".")) { if (!attrPath.empty() && !attrPathS.ends_with(".")) {
lastAttr = evalState->symbols[attrPath.back()]; lastAttr = attrPath.back();
attrPath.pop_back(); attrPath.pop_back();
} }
@ -319,11 +319,11 @@ void completeFlakeRefWithFragment(
if (!attr) continue; if (!attr) continue;
for (auto & attr2 : (*attr)->getAttrs()) { for (auto & attr2 : (*attr)->getAttrs()) {
if (std::string_view(evalState->symbols[attr2]).starts_with(lastAttr)) { if (std::string_view attr2s = attr2; attr2s.starts_with(lastAttr)) {
auto attrPath2 = (*attr)->getAttrPath(attr2); auto attrPath2 = (*attr)->getAttrPath(attr2s);
/* Strip the attrpath prefix. */ /* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", attrPath2));
} }
} }
} }
@ -332,7 +332,7 @@ void completeFlakeRefWithFragment(
attrpaths. */ attrpaths. */
if (fragment.empty()) { if (fragment.empty()) {
for (auto & attrPath : defaultFlakeAttrPaths) { for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); auto attr = root->findAlongAttrPath(parseAttrPath(attrPath));
if (!attr) continue; if (!attr) continue;
completions.add(flakeRefS + "#" + prefixRoot); completions.add(flakeRefS + "#" + prefixRoot);
} }

View file

@ -5,7 +5,7 @@
namespace nix { namespace nix {
static Strings parseAttrPath(std::string_view s) Strings parseAttrPath(std::string_view s)
{ {
Strings res; Strings res;
std::string cur; std::string cur;
@ -31,15 +31,6 @@ static Strings parseAttrPath(std::string_view s)
} }
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
{
std::vector<Symbol> res;
for (auto & a : parseAttrPath(s))
res.push_back(state.symbols.create(a));
return res;
}
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath, std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
Bindings & autoArgs, Value & vIn) Bindings & autoArgs, Value & vIn)
{ {

View file

@ -22,6 +22,6 @@ std::pair<Value *, PosIdx> findAlongAttrPath(
*/ */
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what); std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s); Strings parseAttrPath(std::string_view s);
} }

View file

@ -35,15 +35,11 @@ struct AttrDb
std::unique_ptr<Sync<State>> _state; std::unique_ptr<Sync<State>> _state;
SymbolTable & symbols;
AttrDb( AttrDb(
const Store & cfg, const Store & cfg,
const Hash & fingerprint, const Hash & fingerprint)
SymbolTable & symbols)
: cfg(cfg) : cfg(cfg)
, _state(std::make_unique<Sync<State>>()) , _state(std::make_unique<Sync<State>>())
, symbols(symbols)
{ {
auto state(_state->lock()); auto state(_state->lock());
@ -98,7 +94,7 @@ struct AttrDb
AttrId setAttrs( AttrId setAttrs(
AttrKey key, AttrKey key,
const std::vector<Symbol> & attrs) const fullattr_t & attrs)
{ {
return doSQLite([&]() return doSQLite([&]()
{ {
@ -106,17 +102,17 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::FullAttrs) (AttrType::FullAttrs)
(0, false).exec(); (0, false).exec();
AttrId rowId = state->db.getLastInsertedRowId(); AttrId rowId = state->db.getLastInsertedRowId();
assert(rowId); assert(rowId);
for (auto & attr : attrs) for (auto & attr : attrs.p)
state->insertAttribute.use() state->insertAttribute.use()
(rowId) (rowId)
(symbols[attr]) (attr)
(AttrType::Placeholder) (AttrType::Placeholder)
(0, false).exec(); (0, false).exec();
@ -141,14 +137,14 @@ struct AttrDb
} }
state->insertAttributeWithContext.use() state->insertAttributeWithContext.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::String) (AttrType::String)
(s) (s)
(ctx).exec(); (ctx).exec();
} else { } else {
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::String) (AttrType::String)
(s).exec(); (s).exec();
} }
@ -167,7 +163,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::Bool) (AttrType::Bool)
(b ? 1 : 0).exec(); (b ? 1 : 0).exec();
@ -185,7 +181,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::Int) (AttrType::Int)
(n).exec(); (n).exec();
@ -203,7 +199,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::ListOfStrings) (AttrType::ListOfStrings)
(concatStringsSep("\t", l)).exec(); (concatStringsSep("\t", l)).exec();
@ -219,7 +215,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::Placeholder) (AttrType::Placeholder)
(0, false).exec(); (0, false).exec();
@ -235,7 +231,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::Missing) (AttrType::Missing)
(0, false).exec(); (0, false).exec();
@ -251,7 +247,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::Misc) (AttrType::Misc)
(0, false).exec(); (0, false).exec();
@ -267,7 +263,7 @@ struct AttrDb
state->insertAttribute.use() state->insertAttribute.use()
(key.first) (key.first)
(symbols[key.second]) (key.second)
(AttrType::Failed) (AttrType::Failed)
(0, false).exec(); (0, false).exec();
@ -279,7 +275,7 @@ struct AttrDb
{ {
auto state(_state->lock()); auto state(_state->lock());
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second])); auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
if (!queryAttribute.next()) return {}; if (!queryAttribute.next()) return {};
auto rowId = (AttrId) queryAttribute.getInt(0); auto rowId = (AttrId) queryAttribute.getInt(0);
@ -290,10 +286,10 @@ struct AttrDb
return {{rowId, placeholder_t()}}; return {{rowId, placeholder_t()}};
case AttrType::FullAttrs: { case AttrType::FullAttrs: {
// FIXME: expensive, should separate this out. // FIXME: expensive, should separate this out.
std::vector<Symbol> attrs; fullattr_t attrs;
auto queryAttributes(state->queryAttributes.use()(rowId)); auto queryAttributes(state->queryAttributes.use()(rowId));
while (queryAttributes.next()) while (queryAttributes.next())
attrs.emplace_back(symbols.create(queryAttributes.getStr(0))); attrs.p.emplace_back(queryAttributes.getStr(0));
return {{rowId, attrs}}; return {{rowId, attrs}};
} }
case AttrType::String: { case AttrType::String: {
@ -323,11 +319,10 @@ struct AttrDb
static std::shared_ptr<AttrDb> makeAttrDb( static std::shared_ptr<AttrDb> makeAttrDb(
const Store & cfg, const Store & cfg,
const Hash & fingerprint, const Hash & fingerprint)
SymbolTable & symbols)
{ {
try { try {
return std::make_shared<AttrDb>(cfg, fingerprint, symbols); return std::make_shared<AttrDb>(cfg, fingerprint);
} catch (SQLiteError &) { } catch (SQLiteError &) {
ignoreExceptionExceptInterrupt(); ignoreExceptionExceptInterrupt();
return nullptr; return nullptr;
@ -348,7 +343,7 @@ EvalCache::EvalCache(
std::optional<std::reference_wrapper<const Hash>> useCache, std::optional<std::reference_wrapper<const Hash>> useCache,
EvalState & state, EvalState & state,
RootLoader rootLoader) RootLoader rootLoader)
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr) : db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
, state(state) , state(state)
, rootLoader(rootLoader) , rootLoader(rootLoader)
{ {
@ -382,7 +377,7 @@ AttrCursor::AttrCursor(
AttrKey AttrCursor::getKey() AttrKey AttrCursor::getKey()
{ {
if (!parent) if (!parent)
return {0, root->state.s.epsilon}; return {0, ""};
if (!parent->first->cachedValue) { if (!parent->first->cachedValue) {
parent->first->cachedValue = root->db->getAttr(parent->first->getKey()); parent->first->cachedValue = root->db->getAttr(parent->first->getKey());
assert(parent->first->cachedValue); assert(parent->first->cachedValue);
@ -396,7 +391,7 @@ Value & AttrCursor::getValue()
if (parent) { if (parent) {
auto & vParent = parent->first->getValue(); auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos, "while searching for an attribute"); root->state.forceAttrs(vParent, noPos, "while searching for an attribute");
auto attr = vParent.attrs->get(parent->second); auto attr = vParent.attrs->get(root->state.symbols.create(parent->second));
if (!attr) if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
_value = allocRootValue(attr->value); _value = allocRootValue(attr->value);
@ -406,7 +401,7 @@ Value & AttrCursor::getValue()
return **_value; return **_value;
} }
std::vector<Symbol> AttrCursor::getAttrPath() const std::vector<std::string> AttrCursor::getAttrPath() const
{ {
if (parent) { if (parent) {
auto attrPath = parent->first->getAttrPath(); auto attrPath = parent->first->getAttrPath();
@ -416,21 +411,21 @@ std::vector<Symbol> AttrCursor::getAttrPath() const
return {}; return {};
} }
std::vector<Symbol> AttrCursor::getAttrPath(Symbol name) const std::vector<std::string> AttrCursor::getAttrPath(std::string_view name) const
{ {
auto attrPath = getAttrPath(); auto attrPath = getAttrPath();
attrPath.push_back(name); attrPath.emplace_back(name);
return attrPath; return attrPath;
} }
std::string AttrCursor::getAttrPathStr() const std::string AttrCursor::getAttrPathStr() const
{ {
return concatStringsSep(".", root->state.symbols.resolve(getAttrPath())); return concatStringsSep(".", getAttrPath());
} }
std::string AttrCursor::getAttrPathStr(Symbol name) const std::string AttrCursor::getAttrPathStr(std::string_view name) const
{ {
return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); return concatStringsSep(".", getAttrPath(name));
} }
Value & AttrCursor::forceValue() Value & AttrCursor::forceValue()
@ -469,27 +464,23 @@ Value & AttrCursor::forceValue()
return v; return v;
} }
Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) Suggestions AttrCursor::getSuggestionsForAttr(const std::string & name)
{ {
auto attrNames = getAttrs(); auto attrNames = getAttrs();
std::set<std::string> strAttrNames; return Suggestions::bestMatches({attrNames.begin(), attrNames.end()}, name);
for (auto & name : attrNames)
strAttrNames.insert(root->state.symbols[name]);
return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]);
} }
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name) std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(const std::string & name)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue) { if (cachedValue) {
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) { if (auto attrs = std::get_if<fullattr_t>(&cachedValue->second)) {
for (auto & attr : *attrs) for (auto & attr : attrs->p)
if (attr == name) if (attr == name)
return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), attr)); return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), name));
return nullptr; return nullptr;
} else if (std::get_if<placeholder_t>(&cachedValue->second)) { } else if (std::get_if<placeholder_t>(&cachedValue->second)) {
auto attr = root->db->getAttr({cachedValue->first, name}); auto attr = root->db->getAttr({cachedValue->first, name});
@ -516,7 +507,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
return nullptr; return nullptr;
//errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); //errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
auto attr = v.attrs->get(name); auto attr = v.attrs->get(root->state.symbols.create(name));
if (!attr) { if (!attr) {
if (root->db) { if (root->db) {
@ -538,12 +529,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
} }
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name) ref<AttrCursor> AttrCursor::getAttr(const std::string & name)
{
return maybeGetAttr(root->state.symbols.create(name));
}
ref<AttrCursor> AttrCursor::getAttr(Symbol name)
{ {
auto p = maybeGetAttr(name); auto p = maybeGetAttr(name);
if (!p) if (!p)
@ -551,12 +537,7 @@ ref<AttrCursor> AttrCursor::getAttr(Symbol name)
return ref(p); return ref(p);
} }
ref<AttrCursor> AttrCursor::getAttr(std::string_view name) OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const Strings & attrPath)
{
return getAttr(root->state.symbols.create(name));
}
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
{ {
auto res = shared_from_this(); auto res = shared_from_this();
for (auto & attr : attrPath) { for (auto & attr : attrPath) {
@ -717,15 +698,15 @@ std::vector<std::string> AttrCursor::getListOfStrings()
return res; return res;
} }
std::vector<Symbol> AttrCursor::getAttrs() std::vector<std::string> AttrCursor::getAttrs()
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
cachedValue = root->db->getAttr(getKey()); cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) { if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) { if (auto attrs = std::get_if<fullattr_t>(&cachedValue->second)) {
debug("using cached attrset attribute '%s'", getAttrPathStr()); debug("using cached attrset attribute '%s'", getAttrPathStr());
return *attrs; return attrs->p;
} else } else
root->state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); root->state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
} }
@ -736,18 +717,15 @@ std::vector<Symbol> AttrCursor::getAttrs()
if (v.type() != nAttrs) if (v.type() != nAttrs)
root->state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); root->state.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
std::vector<Symbol> attrs; fullattr_t attrs;
for (auto & attr : *getValue().attrs) for (auto & attr : *getValue().attrs)
attrs.push_back(attr.name); attrs.p.push_back(root->state.symbols[attr.name]);
std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) { std::sort(attrs.p.begin(), attrs.p.end());
std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];
return sa < sb;
});
if (root->db) if (root->db)
cachedValue = {root->db->setAttrs(getKey(), attrs), attrs}; cachedValue = {root->db->setAttrs(getKey(), attrs), attrs};
return attrs; return attrs.p;
} }
bool AttrCursor::isDerivation() bool AttrCursor::isDerivation()
@ -758,7 +736,7 @@ bool AttrCursor::isDerivation()
StorePath AttrCursor::forceDerivation() StorePath AttrCursor::forceDerivation()
{ {
auto aDrvPath = getAttr(root->state.s.drvPath); auto aDrvPath = getAttr("drvPath");
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has /* The eval cache contains 'drvPath', but the actual path has

View file

@ -69,16 +69,17 @@ enum AttrType {
}; };
struct placeholder_t {}; struct placeholder_t {};
struct fullattr_t { std::vector<std::string> p; };
struct missing_t {}; struct missing_t {};
struct misc_t {}; struct misc_t {};
struct failed_t {}; struct failed_t {};
struct int_t { NixInt x; }; struct int_t { NixInt x; };
typedef uint64_t AttrId; typedef uint64_t AttrId;
typedef std::pair<AttrId, Symbol> AttrKey; typedef std::pair<AttrId, std::string> AttrKey;
typedef std::pair<std::string, NixStringContext> string_t; typedef std::pair<std::string, NixStringContext> string_t;
typedef std::variant< typedef std::variant<
std::vector<Symbol>, fullattr_t,
string_t, string_t,
placeholder_t, placeholder_t,
missing_t, missing_t,
@ -94,7 +95,7 @@ class AttrCursor : public std::enable_shared_from_this<AttrCursor>
friend class EvalCache; friend class EvalCache;
ref<EvalCache> root; ref<EvalCache> root;
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent; typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, std::string>> Parent;
Parent parent; Parent parent;
RootValue _value; RootValue _value;
std::optional<std::pair<AttrId, AttrValue>> cachedValue; std::optional<std::pair<AttrId, AttrValue>> cachedValue;
@ -111,29 +112,25 @@ public:
Value * value = nullptr, Value * value = nullptr,
std::optional<std::pair<AttrId, AttrValue>> && cachedValue = {}); std::optional<std::pair<AttrId, AttrValue>> && cachedValue = {});
std::vector<Symbol> getAttrPath() const; std::vector<std::string> getAttrPath() const;
std::vector<Symbol> getAttrPath(Symbol name) const; std::vector<std::string> getAttrPath(std::string_view name) const;
std::string getAttrPathStr() const; std::string getAttrPathStr() const;
std::string getAttrPathStr(Symbol name) const; std::string getAttrPathStr(std::string_view name) const;
Suggestions getSuggestionsForAttr(Symbol name); Suggestions getSuggestionsForAttr(const std::string & name);
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name); std::shared_ptr<AttrCursor> maybeGetAttr(const std::string & name);
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name); ref<AttrCursor> getAttr(const std::string & name);
ref<AttrCursor> getAttr(Symbol name);
ref<AttrCursor> getAttr(std::string_view name);
/** /**
* Get an attribute along a chain of attrsets. Note that this does * Get an attribute along a chain of attrsets. Note that this does
* not auto-call functors or functions. * not auto-call functors or functions.
*/ */
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath); OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const Strings & attrPath);
std::string getString(); std::string getString();
@ -145,7 +142,7 @@ public:
std::vector<std::string> getListOfStrings(); std::vector<std::string> getListOfStrings();
std::vector<Symbol> getAttrs(); std::vector<std::string> getAttrs();
bool isDerivation(); bool isDerivation();

View file

@ -228,7 +228,6 @@ StaticSymbols::StaticSymbols(SymbolTable & symbols)
, recurseForDerivations(symbols.create("recurseForDerivations")) , recurseForDerivations(symbols.create("recurseForDerivations"))
, description(symbols.create("description")) , description(symbols.create("description"))
, self(symbols.create("self")) , self(symbols.create("self"))
, epsilon(symbols.create(""))
, startSet(symbols.create("startSet")) , startSet(symbols.create("startSet"))
, operator_(symbols.create("operator")) , operator_(symbols.create("operator"))
, key(symbols.create("key")) , key(symbols.create("key"))

View file

@ -210,7 +210,7 @@ struct StaticSymbols
ignoreNulls, file, line, column, functor, toString, right, wrong, structuredAttrs, ignoreNulls, file, line, column, functor, toString, right, wrong, structuredAttrs,
allowedReferences, allowedRequisites, disallowedReferences, disallowedRequisites, maxSize, allowedReferences, allowedRequisites, disallowedReferences, disallowedRequisites, maxSize,
maxClosureSize, builder, args, contentAddressed, impure, outputHash, outputHashAlgo, maxClosureSize, builder, args, contentAddressed, impure, outputHash, outputHashAlgo,
outputHashMode, recurseForDerivations, description, self, epsilon, startSet, operator_, key, outputHashMode, recurseForDerivations, description, self, startSet, operator_, key,
path, prefix, outputSpecified; path, prefix, outputSpecified;
const Expr::AstSymbols exprSymbols; const Expr::AstSymbols exprSymbols;

View file

@ -97,15 +97,6 @@ public:
return Symbol(idx + 1); return Symbol(idx + 1);
} }
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
{
std::vector<SymbolStr> result;
result.reserve(symbols.size());
for (auto sym : symbols)
result.push_back((*this)[sym]);
return result;
}
SymbolStr operator[](Symbol s) const SymbolStr operator[](Symbol s) const
{ {
if (s.id == 0 || s.id > store.size()) if (s.id == 0 || s.id > store.size())

View file

@ -59,7 +59,7 @@ UnresolvedApp InstallableValue::toApp()
auto type = cursor->getAttr("type")->getString(); auto type = cursor->getAttr("type")->getString();
std::string expected = !attrPath.empty() && std::string expected = !attrPath.empty() &&
(state->symbols[attrPath[0]] == "apps" || state->symbols[attrPath[0]] == "defaultApp") (attrPath[0] == "apps" || attrPath[0] == "defaultApp")
? "app" : "derivation"; ? "app" : "derivation";
if (type != expected) if (type != expected)
throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected); throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected);
@ -99,11 +99,11 @@ UnresolvedApp InstallableValue::toApp()
else if (type == "derivation") { else if (type == "derivation") {
auto drvPath = cursor->forceDerivation(); auto drvPath = cursor->forceDerivation();
auto outPath = cursor->getAttr(state->s.outPath)->getString(); auto outPath = cursor->getAttr("outPath")->getString();
auto outputName = cursor->getAttr(state->s.outputName)->getString(); auto outputName = cursor->getAttr("outputName")->getString();
auto name = cursor->getAttr(state->s.name)->getString(); auto name = cursor->getAttr("name")->getString();
auto aPname = cursor->maybeGetAttr("pname"); auto aPname = cursor->maybeGetAttr("pname");
auto aMeta = cursor->maybeGetAttr(state->s.meta); auto aMeta = cursor->maybeGetAttr("meta");
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr; auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
auto mainProgram = auto mainProgram =
aMainProgram aMainProgram

View file

@ -1136,8 +1136,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
std::function<bool( std::function<bool(
eval_cache::AttrCursor & visitor, eval_cache::AttrCursor & visitor,
const std::vector<Symbol> &attrPath, std::vector<std::string> attrPath,
const Symbol &attr)> hasContent; const std::string &attr)> hasContent;
// For frameworks it's important that structures are as lazy as possible // For frameworks it's important that structures are as lazy as possible
// to prevent infinite recursions, performance issues and errors that // to prevent infinite recursions, performance issues and errors that
@ -1147,39 +1147,36 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
// so we omit them. // so we omit them.
hasContent = [&]( hasContent = [&](
eval_cache::AttrCursor & visitor, eval_cache::AttrCursor & visitor,
const std::vector<Symbol> &attrPath, std::vector<std::string> attrPath,
const Symbol &attr) -> bool const std::string &attr) -> bool
{ {
auto attrPath2(attrPath); attrPath.push_back(attr);
attrPath2.push_back(attr);
auto attrPathS = state->symbols.resolve(attrPath2);
const auto & attrName = state->symbols[attr];
auto visitor2 = visitor.getAttr(attrName); auto visitor2 = visitor.getAttr(attr);
try { try {
if ((attrPathS[0] == "apps" if ((attrPath[0] == "apps"
|| attrPathS[0] == "checks" || attrPath[0] == "checks"
|| attrPathS[0] == "devShells" || attrPath[0] == "devShells"
|| attrPathS[0] == "legacyPackages" || attrPath[0] == "legacyPackages"
|| attrPathS[0] == "packages") || attrPath[0] == "packages")
&& (attrPathS.size() == 1 || attrPathS.size() == 2)) { && (attrPath.size() == 1 || attrPath.size() == 2)) {
for (const auto &subAttr : visitor2->getAttrs()) { for (const auto &subAttr : visitor2->getAttrs()) {
if (hasContent(*visitor2, attrPath2, subAttr)) { if (hasContent(*visitor2, attrPath, subAttr)) {
return true; return true;
} }
} }
return false; return false;
} }
if ((attrPathS.size() == 1) if ((attrPath.size() == 1)
&& (attrPathS[0] == "formatter" && (attrPath[0] == "formatter"
|| attrPathS[0] == "nixosConfigurations" || attrPath[0] == "nixosConfigurations"
|| attrPathS[0] == "nixosModules" || attrPath[0] == "nixosModules"
|| attrPathS[0] == "overlays" || attrPath[0] == "overlays"
)) { )) {
for (const auto &subAttr : visitor2->getAttrs()) { for (const auto &subAttr : visitor2->getAttrs()) {
if (hasContent(*visitor2, attrPath2, subAttr)) { if (hasContent(*visitor2, attrPath, subAttr)) {
return true; return true;
} }
} }
@ -1198,54 +1195,51 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
std::function<nlohmann::json( std::function<nlohmann::json(
eval_cache::AttrCursor & visitor, eval_cache::AttrCursor & visitor,
const std::vector<Symbol> & attrPath, const std::vector<std::string> & attrPath,
const std::string & headerPrefix, const std::string & headerPrefix,
const std::string & nextPrefix)> visit; const std::string & nextPrefix)> visit;
visit = [&]( visit = [&](
eval_cache::AttrCursor & visitor, eval_cache::AttrCursor & visitor,
const std::vector<Symbol> & attrPath, const std::vector<std::string> & attrPath,
const std::string & headerPrefix, const std::string & headerPrefix,
const std::string & nextPrefix) const std::string & nextPrefix)
-> nlohmann::json -> nlohmann::json
{ {
auto j = nlohmann::json::object(); auto j = nlohmann::json::object();
auto attrPathS = state->symbols.resolve(attrPath);
Activity act(*logger, lvlInfo, actUnknown, Activity act(*logger, lvlInfo, actUnknown,
fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
try { try {
auto recurse = [&]() auto recurse = [&]()
{ {
if (!json) if (!json)
logger->cout("%s", headerPrefix); logger->cout("%s", headerPrefix);
std::vector<Symbol> attrs; std::vector<std::string> attrs;
for (const auto &attr : visitor.getAttrs()) { for (const auto &attr : visitor.getAttrs()) {
if (hasContent(visitor, attrPath, attr)) if (hasContent(visitor, attrPath, attr))
attrs.push_back(attr); attrs.push_back(attr);
} }
for (const auto & [i, attr] : enumerate(attrs)) { for (const auto & [i, attr] : enumerate(attrs)) {
const auto & attrName = state->symbols[attr];
bool last = i + 1 == attrs.size(); bool last = i + 1 == attrs.size();
auto visitor2 = visitor.getAttr(attrName); auto visitor2 = visitor.getAttr(attr);
auto attrPath2(attrPath); auto attrPath2(attrPath);
attrPath2.push_back(attr); attrPath2.push_back(attr);
auto j2 = visit(*visitor2, attrPath2, auto j2 = visit(*visitor2, attrPath2,
fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attrName), fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attr),
nextPrefix + (last ? treeNull : treeLine)); nextPrefix + (last ? treeNull : treeLine));
if (json) j.emplace(attrName, std::move(j2)); if (json) j.emplace(attr, std::move(j2));
} }
}; };
auto showDerivation = [&]() auto showDerivation = [&]()
{ {
auto name = visitor.getAttr(state->s.name)->getString(); auto name = visitor.getAttr("name")->getString();
std::optional<std::string> description; std::optional<std::string> description;
if (auto aMeta = visitor.maybeGetAttr(state->s.meta)) { if (auto aMeta = visitor.maybeGetAttr("meta")) {
if (auto aDescription = aMeta->maybeGetAttr(state->s.description)) if (auto aDescription = aMeta->maybeGetAttr("description"))
description = aDescription->getString(); description = aDescription->getString();
} }
@ -1256,10 +1250,10 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
j.emplace("description", *description); j.emplace("description", *description);
} else { } else {
auto type = auto type =
attrPath.size() == 2 && attrPathS[0] == "devShell" ? "development environment" : attrPath.size() == 2 && attrPath[0] == "devShell" ? "development environment" :
attrPath.size() >= 2 && attrPathS[0] == "devShells" ? "development environment" : attrPath.size() >= 2 && attrPath[0] == "devShells" ? "development environment" :
attrPath.size() == 3 && attrPathS[0] == "checks" ? "derivation" : attrPath.size() == 3 && attrPath[0] == "checks" ? "derivation" :
attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : attrPath.size() >= 1 && attrPath[0] == "hydraJobs" ? "derivation" :
"package"; "package";
std::string output = fmt("%s: %s '%s'", headerPrefix, type, name); std::string output = fmt("%s: %s '%s'", headerPrefix, type, name);
@ -1300,34 +1294,34 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
if (attrPath.size() == 0 if (attrPath.size() == 0
|| (attrPath.size() == 1 && ( || (attrPath.size() == 1 && (
attrPathS[0] == "defaultPackage" attrPath[0] == "defaultPackage"
|| attrPathS[0] == "devShell" || attrPath[0] == "devShell"
|| attrPathS[0] == "formatter" || attrPath[0] == "formatter"
|| attrPathS[0] == "nixosConfigurations" || attrPath[0] == "nixosConfigurations"
|| attrPathS[0] == "nixosModules" || attrPath[0] == "nixosModules"
|| attrPathS[0] == "defaultApp" || attrPath[0] == "defaultApp"
|| attrPathS[0] == "templates" || attrPath[0] == "templates"
|| attrPathS[0] == "overlays")) || attrPath[0] == "overlays"))
|| ((attrPath.size() == 1 || attrPath.size() == 2) || ((attrPath.size() == 1 || attrPath.size() == 2)
&& (attrPathS[0] == "checks" && (attrPath[0] == "checks"
|| attrPathS[0] == "packages" || attrPath[0] == "packages"
|| attrPathS[0] == "devShells" || attrPath[0] == "devShells"
|| attrPathS[0] == "apps")) || attrPath[0] == "apps"))
) )
{ {
recurse(); recurse();
} }
else if ( else if (
(attrPath.size() == 2 && (attrPathS[0] == "defaultPackage" || attrPathS[0] == "devShell" || attrPathS[0] == "formatter")) (attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell" || attrPath[0] == "formatter"))
|| (attrPath.size() == 3 && (attrPathS[0] == "checks" || attrPathS[0] == "packages" || attrPathS[0] == "devShells")) || (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages" || attrPath[0] == "devShells"))
) )
{ {
if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { if (!showAllSystems && attrPath[1] != localSystem) {
if (!json) if (!json)
logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix));
else { else {
logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPath)));
} }
} else { } else {
if (visitor.isDerivation()) if (visitor.isDerivation())
@ -1337,27 +1331,27 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
} }
} }
else if (attrPath.size() > 0 && attrPathS[0] == "hydraJobs") { else if (attrPath.size() > 0 && attrPath[0] == "hydraJobs") {
if (visitor.isDerivation()) if (visitor.isDerivation())
showDerivation(); showDerivation();
else else
recurse(); recurse();
} }
else if (attrPath.size() > 0 && attrPathS[0] == "legacyPackages") { else if (attrPath.size() > 0 && attrPath[0] == "legacyPackages") {
if (attrPath.size() == 1) if (attrPath.size() == 1)
recurse(); recurse();
else if (!showLegacy){ else if (!showLegacy){
if (!json) if (!json)
logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix));
else { else {
logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPath)));
} }
} else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { } else if (!showAllSystems && attrPath[1] != localSystem) {
if (!json) if (!json)
logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix));
else { else {
logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPath)));
} }
} else { } else {
if (visitor.isDerivation()) if (visitor.isDerivation())
@ -1369,8 +1363,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
} }
else if ( else if (
(attrPath.size() == 2 && attrPathS[0] == "defaultApp") || (attrPath.size() == 2 && attrPath[0] == "defaultApp") ||
(attrPath.size() == 3 && attrPathS[0] == "apps")) (attrPath.size() == 3 && attrPath[0] == "apps"))
{ {
auto aType = visitor.maybeGetAttr("type"); auto aType = visitor.maybeGetAttr("type");
if (!aType || aType->getString() != "app") if (!aType || aType->getString() != "app")
@ -1383,8 +1377,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
} }
else if ( else if (
(attrPath.size() == 1 && attrPathS[0] == "defaultTemplate") || (attrPath.size() == 1 && attrPath[0] == "defaultTemplate") ||
(attrPath.size() == 2 && attrPathS[0] == "templates")) (attrPath.size() == 2 && attrPath[0] == "templates"))
{ {
auto description = visitor.getAttr("description")->getString(); auto description = visitor.getAttr("description")->getString();
if (json) { if (json) {
@ -1397,11 +1391,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
else { else {
auto [type, description] = auto [type, description] =
(attrPath.size() == 1 && attrPathS[0] == "overlay") (attrPath.size() == 1 && attrPath[0] == "overlay")
|| (attrPath.size() == 2 && attrPathS[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") : || (attrPath.size() == 2 && attrPath[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") :
attrPath.size() == 2 && attrPathS[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") : attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") :
(attrPath.size() == 1 && attrPathS[0] == "nixosModule") (attrPath.size() == 1 && attrPath[0] == "nixosModule")
|| (attrPath.size() == 2 && attrPathS[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") : || (attrPath.size() == 2 && attrPath[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") :
std::make_pair("unknown", "unknown"); std::make_pair("unknown", "unknown");
if (json) { if (json) {
j.emplace("type", type); j.emplace("type", type);
@ -1410,7 +1404,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
} }
} }
} catch (EvalError & e) { } catch (EvalError & e) {
if (!(attrPath.size() > 0 && attrPathS[0] == "legacyPackages")) if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
throw; throw;
} }

View file

@ -91,33 +91,31 @@ struct CmdSearch : InstallableCommand, MixJSON
uint64_t results = 0; uint64_t results = 0;
std::function<void(eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)> visit; std::function<void(eval_cache::AttrCursor & cursor, const std::vector<std::string> & attrPath, bool initialRecurse)> visit;
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse) visit = [&](eval_cache::AttrCursor & cursor, const std::vector<std::string> & attrPath, bool initialRecurse)
{ {
auto attrPathS = state->symbols.resolve(attrPath);
Activity act(*logger, lvlInfo, actUnknown, Activity act(*logger, lvlInfo, actUnknown,
fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
try { try {
auto recurse = [&]() auto recurse = [&]()
{ {
for (const auto & attr : cursor.getAttrs()) { for (const auto & attr : cursor.getAttrs()) {
auto cursor2 = cursor.getAttr(state->symbols[attr]); auto cursor2 = cursor.getAttr(attr);
auto attrPath2(attrPath); auto attrPath2(attrPath);
attrPath2.push_back(attr); attrPath2.emplace_back(attr);
visit(*cursor2, attrPath2, false); visit(*cursor2, attrPath2, false);
} }
}; };
if (cursor.isDerivation()) { if (cursor.isDerivation()) {
DrvName name(cursor.getAttr(state->s.name)->getString()); DrvName name(cursor.getAttr("name")->getString());
auto aMeta = cursor.maybeGetAttr(state->s.meta); auto aMeta = cursor.maybeGetAttr("meta");
auto aDescription = aMeta ? aMeta->maybeGetAttr(state->s.description) : nullptr; auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr;
auto description = aDescription ? aDescription->getString() : ""; auto description = aDescription ? aDescription->getString() : "";
std::replace(description.begin(), description.end(), '\n', ' '); std::replace(description.begin(), description.end(), '\n', ' ');
auto attrPath2 = concatStringsSep(".", attrPathS); auto attrPath2 = concatStringsSep(".", attrPath);
std::vector<std::smatch> attrPathMatches; std::vector<std::smatch> attrPathMatches;
std::vector<std::smatch> descriptionMatches; std::vector<std::smatch> descriptionMatches;
@ -175,21 +173,21 @@ struct CmdSearch : InstallableCommand, MixJSON
else if ( else if (
attrPath.size() == 0 attrPath.size() == 0
|| (attrPathS[0] == "legacyPackages" && attrPath.size() <= 2) || (attrPath[0] == "legacyPackages" && attrPath.size() <= 2)
|| (attrPathS[0] == "packages" && attrPath.size() <= 2)) || (attrPath[0] == "packages" && attrPath.size() <= 2))
recurse(); recurse();
else if (initialRecurse) else if (initialRecurse)
recurse(); recurse();
else if (attrPathS[0] == "legacyPackages" && attrPath.size() > 2) { else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
auto attr = cursor.maybeGetAttr(state->s.recurseForDerivations); auto attr = cursor.maybeGetAttr("recurseForDerivations");
if (attr && attr->getBool()) if (attr && attr->getBool())
recurse(); recurse();
} }
} catch (EvalError & e) { } catch (EvalError & e) {
if (!(attrPath.size() > 0 && attrPathS[0] == "legacyPackages")) if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
throw; throw;
} }
}; };