lol

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`

+113 -1
+7
lib/tests/modules.sh
··· 299 299 checkConfigError "The option .multiple. is defined multiple times" config.multiple ./raw.nix 300 300 checkConfigOutput "bar" config.priorities ./raw.nix 301 301 302 + # Test that types.optionType merges types correctly 303 + checkConfigOutput '^10$' config.theOption.int ./optionTypeMerging.nix 304 + checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix 305 + 306 + # Test that types.optionType correctly annotates option locations 307 + checkConfigError 'The option .theOption.nested. in .other.nix. is already declared in .optionTypeFile.nix.' config.theOption.nested ./optionTypeFile.nix 308 + 302 309 cat <<EOF 303 310 ====== module tests ====== 304 311 $pass Pass
+28
lib/tests/modules/optionTypeFile.nix
··· 1 + { config, lib, ... }: { 2 + 3 + _file = "optionTypeFile.nix"; 4 + 5 + options.theType = lib.mkOption { 6 + type = lib.types.optionType; 7 + }; 8 + 9 + options.theOption = lib.mkOption { 10 + type = config.theType; 11 + default = {}; 12 + }; 13 + 14 + config.theType = lib.mkMerge [ 15 + (lib.types.submodule { 16 + options.nested = lib.mkOption { 17 + type = lib.types.int; 18 + }; 19 + }) 20 + (lib.types.submodule { 21 + _file = "other.nix"; 22 + options.nested = lib.mkOption { 23 + type = lib.types.str; 24 + }; 25 + }) 26 + ]; 27 + 28 + }
+27
lib/tests/modules/optionTypeMerging.nix
··· 1 + { config, lib, ... }: { 2 + 3 + options.theType = lib.mkOption { 4 + type = lib.types.optionType; 5 + }; 6 + 7 + options.theOption = lib.mkOption { 8 + type = config.theType; 9 + }; 10 + 11 + config.theType = lib.mkMerge [ 12 + (lib.types.submodule { 13 + options.int = lib.mkOption { 14 + type = lib.types.int; 15 + default = 10; 16 + }; 17 + }) 18 + (lib.types.submodule { 19 + options.str = lib.mkOption { 20 + type = lib.types.str; 21 + }; 22 + }) 23 + ]; 24 + 25 + config.theOption.str = "hello"; 26 + 27 + }
+30 -1
lib/types.nix
··· 61 61 boolToString 62 62 ; 63 63 64 - inherit (lib.modules) mergeDefinitions; 64 + inherit (lib.modules) 65 + mergeDefinitions 66 + fixupOptionType 67 + mergeOptionDecls 68 + ; 65 69 outer_types = 66 70 rec { 67 71 isType = type: x: (x._type or "") == type; ··· 523 527 submodule = modules: submoduleWith { 524 528 shorthandOnlyDefinesConfig = true; 525 529 modules = toList modules; 530 + }; 531 + 532 + # The type of a type! 533 + optionType = mkOptionType { 534 + name = "optionType"; 535 + description = "optionType"; 536 + check = value: value._type or null == "option-type"; 537 + merge = loc: defs: 538 + let 539 + # Prepares the type definitions for mergeOptionDecls, which 540 + # annotates submodules types with file locations 541 + optionModules = map ({ value, file }: 542 + { 543 + _file = file; 544 + # There's no way to merge types directly from the module system, 545 + # but we can cheat a bit by just declaring an option with the type 546 + options = lib.mkOption { 547 + type = value; 548 + }; 549 + } 550 + ) defs; 551 + # Merges all the types into a single one, including submodule merging. 552 + # This also propagates file information to all submodules 553 + mergedOption = fixupOptionType loc (mergeOptionDecls loc optionModules); 554 + in mergedOption.type; 526 555 }; 527 556 528 557 submoduleWith =
+7
nixos/doc/manual/development/option-types.section.md
··· 74 74 should only be used when checking, merging and nested evaluation are not 75 75 desirable. 76 76 77 + `types.optionType` 78 + 79 + : The type of an option's type. Its merging operation ensures that nested 80 + options have the correct file location annotated, and that if possible, 81 + multiple option definitions are correctly merged together. The main use 82 + case is as the type of the `_module.freeformType` option. 83 + 77 84 `types.attrs` 78 85 79 86 : A free-form attribute set.
+14
nixos/doc/manual/from_md/development/option-types.section.xml
··· 113 113 </varlistentry> 114 114 <varlistentry> 115 115 <term> 116 + <literal>types.optionType</literal> 117 + </term> 118 + <listitem> 119 + <para> 120 + The type of an option’s type. Its merging operation ensures 121 + that nested options have the correct file location 122 + annotated, and that if possible, multiple option definitions 123 + are correctly merged together. The main use case is as the 124 + type of the <literal>_module.freeformType</literal> option. 125 + </para> 126 + </listitem> 127 + </varlistentry> 128 + <varlistentry> 129 + <term> 116 130 <literal>types.attrs</literal> 117 131 </term> 118 132 <listitem>