From e0780c5cffa7516ad85b885cae7d0c85aed90267 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Tue, 10 Dec 2019 14:31:52 +0100 Subject: [PATCH] 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. --- .../tools/nixos-option/nixos-option.cc | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/nixos/modules/installer/tools/nixos-option/nixos-option.cc b/nixos/modules/installer/tools/nixos-option/nixos-option.cc index 9b92dc829cd1..89f9c88be964 100644 --- a/nixos/modules/installer/tools/nixos-option/nixos-option.cc +++ b/nixos/modules/installer/tools/nixos-option/nixos-option.cc @@ -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(evaluatedType.string.s) == "option"; + return static_cast(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);