summary refs log tree commit diff
diff options
context:
space:
mode:
authorSilvan Mosberger <contact@infinisil.com>2021-12-08 19:02:29 +0100
committerSilvan Mosberger <contact@infinisil.com>2022-03-01 19:31:00 +0100
commit5cbeddfde486ca5524baeaf3da6e8944075cf463 (patch)
treed529522033bcfdc0272fba08e8e3fe1897dbe4e0
parent891e65b0fa1b7c75c42b5404934307f64828e428 (diff)
downloadnixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.tar
nixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.tar.gz
nixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.tar.bz2
nixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.tar.lz
nixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.tar.xz
nixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.tar.zst
nixpkgs-5cbeddfde486ca5524baeaf3da6e8944075cf463.zip
lib.types: Introduce `types.optionType`
This type correctly merges multiple option types together while also
annotating them with file information. In a future commit this will be
used for `_module.freeformType`
-rwxr-xr-xlib/tests/modules.sh7
-rw-r--r--lib/tests/modules/optionTypeFile.nix28
-rw-r--r--lib/tests/modules/optionTypeMerging.nix27
-rw-r--r--lib/types.nix31
-rw-r--r--nixos/doc/manual/development/option-types.section.md7
-rw-r--r--nixos/doc/manual/from_md/development/option-types.section.xml14
6 files changed, 113 insertions, 1 deletions
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index a1c592cf4ef..d11f32e5996 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -299,6 +299,13 @@ checkConfigOutput "10" config.processedToplevel ./raw.nix
 checkConfigError "The option .multiple. is defined multiple times" config.multiple ./raw.nix
 checkConfigOutput "bar" config.priorities ./raw.nix
 
+# Test that types.optionType merges types correctly
+checkConfigOutput '^10$' config.theOption.int ./optionTypeMerging.nix
+checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix
+
+# Test that types.optionType correctly annotates option locations
+checkConfigError 'The option .theOption.nested. in .other.nix. is already declared in .optionTypeFile.nix.' config.theOption.nested ./optionTypeFile.nix
+
 cat <<EOF
 ====== module tests ======
 $pass Pass
diff --git a/lib/tests/modules/optionTypeFile.nix b/lib/tests/modules/optionTypeFile.nix
new file mode 100644
index 00000000000..6015d59a72c
--- /dev/null
+++ b/lib/tests/modules/optionTypeFile.nix
@@ -0,0 +1,28 @@
+{ config, lib, ... }: {
+
+  _file = "optionTypeFile.nix";
+
+  options.theType = lib.mkOption {
+    type = lib.types.optionType;
+  };
+
+  options.theOption = lib.mkOption {
+    type = config.theType;
+    default = {};
+  };
+
+  config.theType = lib.mkMerge [
+    (lib.types.submodule {
+      options.nested = lib.mkOption {
+        type = lib.types.int;
+      };
+    })
+    (lib.types.submodule {
+      _file = "other.nix";
+      options.nested = lib.mkOption {
+        type = lib.types.str;
+      };
+    })
+  ];
+
+}
diff --git a/lib/tests/modules/optionTypeMerging.nix b/lib/tests/modules/optionTypeMerging.nix
new file mode 100644
index 00000000000..74a620c4620
--- /dev/null
+++ b/lib/tests/modules/optionTypeMerging.nix
@@ -0,0 +1,27 @@
+{ config, lib, ... }: {
+
+  options.theType = lib.mkOption {
+    type = lib.types.optionType;
+  };
+
+  options.theOption = lib.mkOption {
+    type = config.theType;
+  };
+
+  config.theType = lib.mkMerge [
+    (lib.types.submodule {
+      options.int = lib.mkOption {
+        type = lib.types.int;
+        default = 10;
+      };
+    })
+    (lib.types.submodule {
+      options.str = lib.mkOption {
+        type = lib.types.str;
+      };
+    })
+  ];
+
+  config.theOption.str = "hello";
+
+}
diff --git a/lib/types.nix b/lib/types.nix
index c47a35cd0d6..acc7eac104e 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -61,7 +61,11 @@ let
     boolToString
     ;
 
-  inherit (lib.modules) mergeDefinitions;
+  inherit (lib.modules)
+    mergeDefinitions
+    fixupOptionType
+    mergeOptionDecls
+    ;
   outer_types =
 rec {
   isType = type: x: (x._type or "") == type;
@@ -525,6 +529,31 @@ rec {
       modules = toList modules;
     };
 
+    # The type of a type!
+    optionType = mkOptionType {
+      name = "optionType";
+      description = "optionType";
+      check = value: value._type or null == "option-type";
+      merge = loc: defs:
+        let
+          # Prepares the type definitions for mergeOptionDecls, which
+          # annotates submodules types with file locations
+          optionModules = map ({ value, file }:
+            {
+              _file = file;
+              # There's no way to merge types directly from the module system,
+              # but we can cheat a bit by just declaring an option with the type
+              options = lib.mkOption {
+                type = value;
+              };
+            }
+          ) defs;
+          # Merges all the types into a single one, including submodule merging.
+          # This also propagates file information to all submodules
+          mergedOption = fixupOptionType loc (mergeOptionDecls loc optionModules);
+        in mergedOption.type;
+    };
+
     submoduleWith =
       { modules
       , specialArgs ? {}
diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md
index 78ace62e8f1..071e7751eb6 100644
--- a/nixos/doc/manual/development/option-types.section.md
+++ b/nixos/doc/manual/development/option-types.section.md
@@ -74,6 +74,13 @@ merging is handled.
     should only be used when checking, merging and nested evaluation are not
     desirable.
 
+`types.optionType`
+
+:   The type of an option's type. Its merging operation ensures that nested
+    options have the correct file location annotated, and that if possible,
+    multiple option definitions are correctly merged together. The main use
+    case is as the type of the `_module.freeformType` option.
+
 `types.attrs`
 
 :   A free-form attribute set.
diff --git a/nixos/doc/manual/from_md/development/option-types.section.xml b/nixos/doc/manual/from_md/development/option-types.section.xml
index 90ef05a24e7..50a3da0ef5b 100644
--- a/nixos/doc/manual/from_md/development/option-types.section.xml
+++ b/nixos/doc/manual/from_md/development/option-types.section.xml
@@ -113,6 +113,20 @@
       </varlistentry>
       <varlistentry>
         <term>
+          <literal>types.optionType</literal>
+        </term>
+        <listitem>
+          <para>
+            The type of an option’s type. Its merging operation ensures
+            that nested options have the correct file location
+            annotated, and that if possible, multiple option definitions
+            are correctly merged together. The main use case is as the
+            type of the <literal>_module.freeformType</literal> option.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
           <literal>types.attrs</literal>
         </term>
         <listitem>