nixos/nixos-option: Show values inside aggregate options uniformly
1. This makes aggregates of submodules (including the very important "nixos-option users.users.<username>" case) behave the same way as any other you-need-to-keep-typing-to-get-to-an-option-leaf (eg: "nixos-option environment"). Beforee0780c5
: $ nixos-option users.users.root error: At 'root' in path 'users.users.root': Attribute not found An error occurred while looking for attribute names. Are you sure that 'users.users.root' exists? Aftere0780c5
but before this change, this query just printed out a raw thing, which is behavior that belongs in "nix eval", "nix-instantiate --eval", or "nix repl <<<": $ nixos-option users.users.root { _module = { args = { name = "root"; }; check = true; }; createHome = false; cryptHomeLuks = null; description = "System administrator"; ... After this change: $ nixos-option users.users.root This attribute set contains: createHome cryptHomeLuks description extraGroups group hashedPassword ... 2. For aggregates of other types (not submodules), print out the option that contains them rather than printing an error message. Before: $ nixos-option environment.shellAliases.l error: At 'l' in path 'environment.shellAliases.l': Attribute not found An error occurred while looking for attribute names. Are you sure that 'environment.shellAliases.l' exists? After: $ nixos-option environment.shellAliases.l Note: showing environment.shellAliases instead of environment.shellAliases.l Value: { l = "ls -alh"; ll = "ls -l"; ls = "ls --color=tty"; } ...
This commit is contained in:
parent
e0780c5cff
commit
09ac7cb55f
|
@ -106,14 +106,13 @@ struct Context
|
|||
{
|
||||
Context(EvalState & state, Bindings & autoArgs, Value optionsRoot, Value configRoot)
|
||||
: state(state), autoArgs(autoArgs), optionsRoot(optionsRoot), configRoot(configRoot),
|
||||
underscoreType(state.symbols.create("_type")), submoduleAttr(state.symbols.create("__nixos-option-submodule-attr"))
|
||||
underscoreType(state.symbols.create("_type"))
|
||||
{}
|
||||
EvalState & state;
|
||||
Bindings & autoArgs;
|
||||
Value optionsRoot;
|
||||
Value configRoot;
|
||||
Symbol underscoreType;
|
||||
Symbol submoduleAttr;
|
||||
};
|
||||
|
||||
Value evaluateValue(Context & ctx, Value & v)
|
||||
|
@ -127,7 +126,7 @@ Value evaluateValue(Context & ctx, Value & v)
|
|||
return called;
|
||||
}
|
||||
|
||||
bool isType(Context & ctx, const Value & v, const std::string & type)
|
||||
bool isOption(Context & ctx, const Value & v)
|
||||
{
|
||||
if (v.type != tAttrs) {
|
||||
return false;
|
||||
|
@ -141,17 +140,12 @@ bool isType(Context & ctx, const Value & v, const std::string & type)
|
|||
if (evaluatedType.type != tString) {
|
||||
return false;
|
||||
}
|
||||
return static_cast<std::string>(evaluatedType.string.s) == type;
|
||||
return static_cast<std::string>(evaluatedType.string.s) == "option";
|
||||
} 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
|
||||
|
@ -300,9 +294,11 @@ void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
|
|||
Out attrsOut(out, "{", "}", v.attrs->size());
|
||||
for (const auto & a : v.attrs->lexicographicOrder()) {
|
||||
std::string name = a->name;
|
||||
attrsOut << name << " = ";
|
||||
printValue(ctx, attrsOut, *a->value, appendPath(path, name));
|
||||
attrsOut << ";" << Out::sep;
|
||||
if (!forbiddenRecursionName(name)) {
|
||||
attrsOut << name << " = ";
|
||||
printValue(ctx, attrsOut, *a->value, appendPath(path, name));
|
||||
attrsOut << ";" << Out::sep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -503,10 +499,16 @@ Value getSubOptions(Context & ctx, Value & option)
|
|||
|
||||
// Carefully walk an option path, looking for sub-options when a path walks past
|
||||
// an option value.
|
||||
Value findAlongOptionPath(Context & ctx, const std::string & path)
|
||||
struct FindAlongOptionPathRet
|
||||
{
|
||||
Value option;
|
||||
std::string path;
|
||||
};
|
||||
FindAlongOptionPathRet findAlongOptionPath(Context & ctx, const std::string & path)
|
||||
{
|
||||
Strings tokens = parseAttrPath(path);
|
||||
Value v = ctx.optionsRoot;
|
||||
std::string processedPath;
|
||||
for (auto i = tokens.begin(); i != tokens.end(); i++) {
|
||||
const auto & attr = *i;
|
||||
try {
|
||||
|
@ -518,48 +520,42 @@ Value findAlongOptionPath(Context & ctx, const std::string & path)
|
|||
if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
|
||||
v = getSubOptions(ctx, v);
|
||||
}
|
||||
if (isOption(ctx, v) && isAggregateOptionType(ctx, v) && !lastAttribute) {
|
||||
v = getSubOptions(ctx, v);
|
||||
if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) {
|
||||
auto subOptions = getSubOptions(ctx, v);
|
||||
if (lastAttribute && subOptions.attrs->empty()) {
|
||||
break;
|
||||
}
|
||||
v = subOptions;
|
||||
// Note that we've consumed attr, but didn't actually use it. This is the path component that's looked
|
||||
// up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name".
|
||||
} else if (v.type != tAttrs) {
|
||||
throw OptionPathError("Value is %s while a set was expected", showType(v));
|
||||
} else {
|
||||
auto symbol = ctx.state.symbols.create(attr);
|
||||
const auto & next = v.attrs->find(symbol);
|
||||
const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
|
||||
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;
|
||||
}
|
||||
processedPath = appendPath(processedPath, attr);
|
||||
} catch (OptionPathError & e) {
|
||||
throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
|
||||
}
|
||||
}
|
||||
return v;
|
||||
return {v, processedPath};
|
||||
}
|
||||
|
||||
void printOne(Context & ctx, Out & out, const std::string & path)
|
||||
{
|
||||
try {
|
||||
Value option = findAlongOptionPath(ctx, path);
|
||||
auto result = findAlongOptionPath(ctx, path);
|
||||
Value & option = result.option;
|
||||
option = evaluateValue(ctx, option);
|
||||
if (isType(ctx, option, ctx.submoduleAttr)) {
|
||||
printAttr(ctx, out, path, ctx.configRoot);
|
||||
} else if (isOption(ctx, option)) {
|
||||
printOption(ctx, out, path, option);
|
||||
if (path != result.path) {
|
||||
out << "Note: showing " << result.path << " instead of " << path << "\n";
|
||||
}
|
||||
if (isOption(ctx, option)) {
|
||||
printOption(ctx, out, result.path, option);
|
||||
} else {
|
||||
printListing(out, option);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue