diff options
author | Silvan Mosberger <contact@infinisil.com> | 2022-03-02 18:29:44 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-02 18:29:44 +0100 |
commit | c1dfec2be66a995dbf6c73e01b3d07a2adf5788b (patch) | |
tree | 54b7f1d5a3b522a624a0b1093a6c27294291bf81 /lib | |
parent | bff70d25ffb12e24451f3015ca5aae83284b34ba (diff) | |
parent | 023fa7b9239e645f7dfcbbe3ee82ab0093f9978d (diff) |
Merge pull request #149689 from Infinisil/types-type
Introduce `types.optionType` and use it for `freeformType`
Diffstat (limited to 'lib')
-rw-r--r-- | lib/modules.nix | 3 | ||||
-rwxr-xr-x | lib/tests/modules.sh | 12 | ||||
-rw-r--r-- | lib/tests/modules/freeform-submodules.nix | 22 | ||||
-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 |
6 files changed, 120 insertions, 3 deletions
diff --git a/lib/modules.nix b/lib/modules.nix index e9fc1007fc20..79d54e4a5387 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -151,8 +151,7 @@ rec { }; _module.freeformType = mkOption { - # Disallow merging for now, but could be implemented nicely with a `types.optionType` - type = types.nullOr (types.uniq types.attrs); + type = types.nullOr types.optionType; internal = true; default = null; description = '' diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index a1c592cf4ef0..e4bb7ad21900 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -240,6 +240,11 @@ checkConfigOutput '^"24"$' config.foo ./freeform-attrsOf.nix ./freeform-str-dep- checkConfigError 'infinite recursion encountered' config.foo ./freeform-attrsOf.nix ./freeform-unstr-dep-str.nix checkConfigError 'The option .* is used but not defined' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix checkConfigOutput '^"24"$' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix ./define-value-string.nix +# submodules in freeformTypes should have their locations annotated +checkConfigOutput '/freeform-submodules.nix"$' config.fooDeclarations.0 ./freeform-submodules.nix +# freeformTypes can get merged using `types.type`, including submodules +checkConfigOutput '^10$' config.free.xxx.foo ./freeform-submodules.nix +checkConfigOutput '^10$' config.free.yyy.bar ./freeform-submodules.nix ## types.anything # Check that attribute sets are merged recursively @@ -299,6 +304,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/freeform-submodules.nix b/lib/tests/modules/freeform-submodules.nix new file mode 100644 index 000000000000..3910435a7b5c --- /dev/null +++ b/lib/tests/modules/freeform-submodules.nix @@ -0,0 +1,22 @@ +{ lib, options, ... }: with lib.types; { + + options.fooDeclarations = lib.mkOption { + default = (options.free.type.getSubOptions [])._freeformOptions.foo.declarations; + }; + + options.free = lib.mkOption { + type = submodule { + config._module.freeformType = lib.mkMerge [ + (attrsOf (submodule { + options.foo = lib.mkOption {}; + })) + (attrsOf (submodule { + options.bar = lib.mkOption {}; + })) + ]; + }; + }; + + config.free.xxx.foo = 10; + config.free.yyy.bar = 10; +} diff --git a/lib/tests/modules/optionTypeFile.nix b/lib/tests/modules/optionTypeFile.nix new file mode 100644 index 000000000000..6015d59a72c9 --- /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 000000000000..74a620c4620c --- /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 c47a35cd0d67..acc7eac104ee 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 ? {} |