nixos/nixos-option: fix evaluator to render a full submodule entry

When running e.g. `nixos-option users.users.ma27`, the evaluation breaks
since `ma27` is the attribute name in `attrsOf (submodule {})`, but not
a part of the option tree and therefore breaks with the following
errors:

```
error: At 'ma27' in path 'users.users.ma27': Attribute not found
An error occurred while looking for attribute names. Are you sure that 'users.users.ma27' exists?
```

This happens since the option evaluator expects that either the option
exists or the option is a submodule and the "next" token in the
attribute path points to an option (e.g. `users.users.ma27.createHome`).

This patch checks in the `Attribute not found` condition if the attribute-path
actually exists in the config tree. If that's true, a dummy-attrset is created
which contains `{_type = "__nixos-option-submodule-attr";}`, in that case, the
entire entry of the submodule will be displayed.
This commit is contained in:
Maximilian Bosch 2019-12-10 14:31:52 +01:00
parent e16df73d30
commit e0780c5cff
No known key found for this signature in database
GPG Key ID: 091DBF4D1FC46B8E

View File

@ -106,13 +106,14 @@ struct Context
{
Context(EvalState & state, Bindings & autoArgs, Value optionsRoot, Value configRoot)
: state(state), autoArgs(autoArgs), optionsRoot(optionsRoot), configRoot(configRoot),
underscoreType(state.symbols.create("_type"))
underscoreType(state.symbols.create("_type")), submoduleAttr(state.symbols.create("__nixos-option-submodule-attr"))
{}
EvalState & state;
Bindings & autoArgs;
Value optionsRoot;
Value configRoot;
Symbol underscoreType;
Symbol submoduleAttr;
};
Value evaluateValue(Context & ctx, Value & v)
@ -126,26 +127,31 @@ Value evaluateValue(Context & ctx, Value & v)
return called;
}
bool isOption(Context & ctx, const Value & v)
bool isType(Context & ctx, const Value & v, const std::string & type)
{
if (v.type != tAttrs) {
return false;
}
const auto & atualType = v.attrs->find(ctx.underscoreType);
if (atualType == v.attrs->end()) {
const auto & actualType = v.attrs->find(ctx.underscoreType);
if (actualType == v.attrs->end()) {
return false;
}
try {
Value evaluatedType = evaluateValue(ctx, *atualType->value);
Value evaluatedType = evaluateValue(ctx, *actualType->value);
if (evaluatedType.type != tString) {
return false;
}
return static_cast<std::string>(evaluatedType.string.s) == "option";
return static_cast<std::string>(evaluatedType.string.s) == type;
} catch (Error &) {
return false;
}
}
bool isOption(Context & ctx, const Value & v)
{
return isType(ctx, v, "option");
}
// Add quotes to a component of a path.
// These are needed for paths like:
// fileSystems."/".fsType
@ -519,11 +525,24 @@ Value findAlongOptionPath(Context & ctx, const std::string & path)
} else if (v.type != tAttrs) {
throw OptionPathError("Value is %s while a set was expected", showType(v));
} else {
const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
auto symbol = ctx.state.symbols.create(attr);
const auto & next = v.attrs->find(symbol);
if (next == v.attrs->end()) {
try {
const auto & value = findAlongAttrPath(ctx.state, path, ctx.autoArgs, ctx.configRoot);
Value &dummyOpt = *ctx.state.allocValue();
ctx.state.mkAttrs(dummyOpt, 1);
Value *type = ctx.state.allocAttr(dummyOpt, ctx.state.symbols.create("_type"));
nix::mkString(*type, ctx.submoduleAttr);
v = dummyOpt;
break;
} catch (Error & e) {
// do nothing
}
throw OptionPathError("Attribute not found", attr, path);
} else {
v = *next->value;
}
v = *next->value;
}
} catch (OptionPathError & e) {
throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
@ -537,7 +556,9 @@ void printOne(Context & ctx, Out & out, const std::string & path)
try {
Value option = findAlongOptionPath(ctx, path);
option = evaluateValue(ctx, option);
if (isOption(ctx, option)) {
if (isType(ctx, option, ctx.submoduleAttr)) {
printAttr(ctx, out, path, ctx.configRoot);
} else if (isOption(ctx, option)) {
printOption(ctx, out, path, option);
} else {
printListing(out, option);