From 42d3b54f0dab73e42e6643e0a3c0ee140b6c8112 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 29 Jan 2024 07:56:35 +0100 Subject: [PATCH] lib.types.attrTag: Take options instead of types --- lib/tests/modules/types-attrTag.nix | 49 ++++++++++++++++++++--------- lib/types.nix | 27 ++++++++++++---- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/lib/tests/modules/types-attrTag.nix b/lib/tests/modules/types-attrTag.nix index 1a0a0f7ba01e..b105d01512dc 100644 --- a/lib/tests/modules/types-attrTag.nix +++ b/lib/tests/modules/types-attrTag.nix @@ -8,37 +8,55 @@ in intStrings = mkOption { type = types.attrsOf (types.attrTag { - left = types.int; - right = types.str; + left = mkOption { + type = types.int; + }; + right = mkOption { + type = types.str; + }; }); }; nested = mkOption { type = types.attrTag { - left = types.int; - right = types.attrTag { - left = types.int; - right = types.str; + left = mkOption { + type = types.int; + }; + right = mkOption { + type = types.attrTag { + left = mkOption { + type = types.int; + }; + right = mkOption { + type = types.str; + }; + }; }; }; }; merged = mkOption { type = types.attrsOf ( types.attrTag { - yay = types.int; + yay = mkOption { + type = types.int; + }; } ); }; submodules = mkOption { type = types.attrsOf ( types.attrTag { - foo = types.submodule { - options = { - bar = mkOption { - type = types.int; + foo = mkOption { + type = types.submodule { + options = { + bar = mkOption { + type = types.int; + }; }; }; }; - qux = types.str; + qux = mkOption { + type = types.str; + }; } ); }; @@ -50,7 +68,9 @@ in options.merged = mkOption { type = types.attrsOf ( types.attrTag { - nay = types.bool; + nay = mkOption { + type = types.bool; + }; } ); }; @@ -76,8 +96,7 @@ in assert config.merged.positive.yay == 100; # assert lib.foldl' (a: b: builtins.trace b a) true (lib.attrNames config.docs); assert config.docs."submodules..foo.bar".type == "signed integer"; - # It's not an option, so we can't render it as such. Something would be nice though. - assert ! (config.docs?"submodules..qux"); + assert config.docs."submodules..qux".type == "string"; true; }; } diff --git a/lib/types.nix b/lib/types.nix index 5286ce76862e..2c6845178852 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -65,6 +65,11 @@ let fixupOptionType mergeOptionDecls ; + + inAttrPosSuffix = v: name: + let pos = builtins.unsafeGetAttrPos name v; in + if pos == null then "" else " at ${pos.file}:${toString pos.line}:${toString pos.column}"; + outer_types = rec { __attrsFailEvaluation = true; @@ -616,8 +621,16 @@ rec { attrTag = tags: attrTagWith { inherit tags; }; - attrTagWith = { tags }: + attrTagWith = args@{ tags }: let + tags = + mapAttrs + (n: opt: + builtins.addErrorContext "while checking that attrTag tag ${lib.strings.escapeNixIdentifier n} is an option with a type${inAttrPosSuffix args.tags n}" ( + assert opt._type == "option"; + opt + )) + args.tags; choicesStr = concatMapStringsSep ", " lib.strings.escapeNixIdentifier (attrNames tags); in mkOptionType { @@ -625,10 +638,12 @@ rec { description = "attribute-tagged union of ${choicesStr}"; getSubOptions = prefix: mapAttrs - (tagName: tagType: - tagType.getSubOptions (prefix ++ [ tagName ])) + (tagName: tagOption: { + "${lib.showOption prefix}" = + tagOption // { loc = prefix ++ [ tagName ]; }; + }) tags; - substSubModules = m: attrTagWith { tags = mapAttrs (n: v: v.substSubModules m) tags; }; + substSubModules = m: attrTagWith { tags = mapAttrs (n: opt: opt // { type = (opt.type or types.unspecified).substSubModules m; }) tags; }; check = v: isAttrs v && length (attrNames v) == 1 && tags?${head (attrNames v)}; merge = loc: defs: let @@ -644,11 +659,11 @@ rec { if tags?${choice} then { ${choice} = - (mergeDefinitions + (lib.modules.evalOptionValue (loc ++ [choice]) tags.${choice} checkedValueDefs - ).mergedValue; + ).value; } else throw "The option `${showOption loc}` is defined as ${lib.strings.escapeNixIdentifier choice}, but ${lib.strings.escapeNixIdentifier choice} is not among the valid choices (${choicesStr}). Value ${choice} was defined in ${showFiles (getFiles defs)}."; nestedTypes = tags;