diff options
author | Silvan Mosberger <contact@infinisil.com> | 2021-12-08 19:02:29 +0100 |
---|---|---|
committer | Silvan Mosberger <contact@infinisil.com> | 2022-03-01 19:31:00 +0100 |
commit | 5cbeddfde486ca5524baeaf3da6e8944075cf463 (patch) | |
tree | d529522033bcfdc0272fba08e8e3fe1897dbe4e0 | |
parent | 891e65b0fa1b7c75c42b5404934307f64828e428 (diff) | |
download | nixpkgs-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-x | lib/tests/modules.sh | 7 | ||||
-rw-r--r-- | lib/tests/modules/optionTypeFile.nix | 28 | ||||
-rw-r--r-- | lib/tests/modules/optionTypeMerging.nix | 27 | ||||
-rw-r--r-- | lib/types.nix | 31 | ||||
-rw-r--r-- | nixos/doc/manual/development/option-types.section.md | 7 | ||||
-rw-r--r-- | nixos/doc/manual/from_md/development/option-types.section.xml | 14 |
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> |