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"). Before e0780c5: $ 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? After e0780c5 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)
|
Context(EvalState & state, Bindings & autoArgs, Value optionsRoot, Value configRoot)
|
||||||
: state(state), autoArgs(autoArgs), optionsRoot(optionsRoot), configRoot(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;
|
EvalState & state;
|
||||||
Bindings & autoArgs;
|
Bindings & autoArgs;
|
||||||
Value optionsRoot;
|
Value optionsRoot;
|
||||||
Value configRoot;
|
Value configRoot;
|
||||||
Symbol underscoreType;
|
Symbol underscoreType;
|
||||||
Symbol submoduleAttr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Value evaluateValue(Context & ctx, Value & v)
|
Value evaluateValue(Context & ctx, Value & v)
|
||||||
@ -127,7 +126,7 @@ Value evaluateValue(Context & ctx, Value & v)
|
|||||||
return called;
|
return called;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isType(Context & ctx, const Value & v, const std::string & type)
|
bool isOption(Context & ctx, const Value & v)
|
||||||
{
|
{
|
||||||
if (v.type != tAttrs) {
|
if (v.type != tAttrs) {
|
||||||
return false;
|
return false;
|
||||||
@ -141,17 +140,12 @@ bool isType(Context & ctx, const Value & v, const std::string & type)
|
|||||||
if (evaluatedType.type != tString) {
|
if (evaluatedType.type != tString) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return static_cast<std::string>(evaluatedType.string.s) == type;
|
return static_cast<std::string>(evaluatedType.string.s) == "option";
|
||||||
} catch (Error &) {
|
} catch (Error &) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isOption(Context & ctx, const Value & v)
|
|
||||||
{
|
|
||||||
return isType(ctx, v, "option");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add quotes to a component of a path.
|
// Add quotes to a component of a path.
|
||||||
// These are needed for paths like:
|
// These are needed for paths like:
|
||||||
// fileSystems."/".fsType
|
// fileSystems."/".fsType
|
||||||
@ -300,11 +294,13 @@ void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
|
|||||||
Out attrsOut(out, "{", "}", v.attrs->size());
|
Out attrsOut(out, "{", "}", v.attrs->size());
|
||||||
for (const auto & a : v.attrs->lexicographicOrder()) {
|
for (const auto & a : v.attrs->lexicographicOrder()) {
|
||||||
std::string name = a->name;
|
std::string name = a->name;
|
||||||
|
if (!forbiddenRecursionName(name)) {
|
||||||
attrsOut << name << " = ";
|
attrsOut << name << " = ";
|
||||||
printValue(ctx, attrsOut, *a->value, appendPath(path, name));
|
printValue(ctx, attrsOut, *a->value, appendPath(path, name));
|
||||||
attrsOut << ";" << Out::sep;
|
attrsOut << ";" << Out::sep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void multiLineStringEscape(Out & out, const std::string & s)
|
void multiLineStringEscape(Out & out, const std::string & s)
|
||||||
{
|
{
|
||||||
@ -503,10 +499,16 @@ Value getSubOptions(Context & ctx, Value & option)
|
|||||||
|
|
||||||
// Carefully walk an option path, looking for sub-options when a path walks past
|
// Carefully walk an option path, looking for sub-options when a path walks past
|
||||||
// an option value.
|
// 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);
|
Strings tokens = parseAttrPath(path);
|
||||||
Value v = ctx.optionsRoot;
|
Value v = ctx.optionsRoot;
|
||||||
|
std::string processedPath;
|
||||||
for (auto i = tokens.begin(); i != tokens.end(); i++) {
|
for (auto i = tokens.begin(); i != tokens.end(); i++) {
|
||||||
const auto & attr = *i;
|
const auto & attr = *i;
|
||||||
try {
|
try {
|
||||||
@ -518,48 +520,42 @@ Value findAlongOptionPath(Context & ctx, const std::string & path)
|
|||||||
if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
|
if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
|
||||||
v = getSubOptions(ctx, v);
|
v = getSubOptions(ctx, v);
|
||||||
}
|
}
|
||||||
if (isOption(ctx, v) && isAggregateOptionType(ctx, v) && !lastAttribute) {
|
if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) {
|
||||||
v = getSubOptions(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
|
// 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".
|
// 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) {
|
} else if (v.type != tAttrs) {
|
||||||
throw OptionPathError("Value is %s while a set was expected", showType(v));
|
throw OptionPathError("Value is %s while a set was expected", showType(v));
|
||||||
} else {
|
} else {
|
||||||
auto symbol = ctx.state.symbols.create(attr);
|
const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
|
||||||
const auto & next = v.attrs->find(symbol);
|
|
||||||
if (next == v.attrs->end()) {
|
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);
|
throw OptionPathError("Attribute not found", attr, path);
|
||||||
} else {
|
}
|
||||||
v = *next->value;
|
v = *next->value;
|
||||||
}
|
}
|
||||||
}
|
processedPath = appendPath(processedPath, attr);
|
||||||
} catch (OptionPathError & e) {
|
} catch (OptionPathError & e) {
|
||||||
throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
|
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)
|
void printOne(Context & ctx, Out & out, const std::string & path)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Value option = findAlongOptionPath(ctx, path);
|
auto result = findAlongOptionPath(ctx, path);
|
||||||
|
Value & option = result.option;
|
||||||
option = evaluateValue(ctx, option);
|
option = evaluateValue(ctx, option);
|
||||||
if (isType(ctx, option, ctx.submoduleAttr)) {
|
if (path != result.path) {
|
||||||
printAttr(ctx, out, path, ctx.configRoot);
|
out << "Note: showing " << result.path << " instead of " << path << "\n";
|
||||||
} else if (isOption(ctx, option)) {
|
}
|
||||||
printOption(ctx, out, path, option);
|
if (isOption(ctx, option)) {
|
||||||
|
printOption(ctx, out, result.path, option);
|
||||||
} else {
|
} else {
|
||||||
printListing(out, option);
|
printListing(out, option);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user