about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorRobert Hensing <robert@roberthensing.nl>2024-01-29 07:56:35 +0100
committerRobert Hensing <robert@roberthensing.nl>2024-04-04 11:54:45 +0200
commit42d3b54f0dab73e42e6643e0a3c0ee140b6c8112 (patch)
tree551e00961598c1758f8367c4d7ab9a06f9f7db13 /lib
parent5b49672af44875df0449f7ff1b55965eedeec1da (diff)
lib.types.attrTag: Take options instead of types
Diffstat (limited to 'lib')
-rw-r--r--lib/tests/modules/types-attrTag.nix49
-rw-r--r--lib/types.nix27
2 files changed, 55 insertions, 21 deletions
diff --git a/lib/tests/modules/types-attrTag.nix b/lib/tests/modules/types-attrTag.nix
index 1a0a0f7ba01e4..b105d01512dc5 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.<name>.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.<name>.qux");
+      assert config.docs."submodules.<name>.qux".type == "string";
       true;
   };
 }
diff --git a/lib/types.nix b/lib/types.nix
index 5286ce76862e2..2c6845178852c 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;