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`

Changed files
+113 -1
lib
nixos
doc
manual
development
from_md
+7
lib/tests/modules.sh
···
checkConfigError "The option .multiple. is defined multiple times" config.multiple ./raw.nix
checkConfigOutput "bar" config.priorities ./raw.nix
cat <<EOF
====== module tests ======
$pass Pass
···
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
+28
lib/tests/modules/optionTypeFile.nix
···
···
+
{ 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;
+
};
+
})
+
];
+
+
}
+27
lib/tests/modules/optionTypeMerging.nix
···
···
+
{ 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";
+
+
}
+30 -1
lib/types.nix
···
boolToString
;
-
inherit (lib.modules) mergeDefinitions;
outer_types =
rec {
isType = type: x: (x._type or "") == type;
···
submodule = modules: submoduleWith {
shorthandOnlyDefinesConfig = true;
modules = toList modules;
};
submoduleWith =
···
boolToString
;
+
inherit (lib.modules)
+
mergeDefinitions
+
fixupOptionType
+
mergeOptionDecls
+
;
outer_types =
rec {
isType = type: x: (x._type or "") == type;
···
submodule = modules: submoduleWith {
shorthandOnlyDefinesConfig = true;
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 =
+7
nixos/doc/manual/development/option-types.section.md
···
should only be used when checking, merging and nested evaluation are not
desirable.
`types.attrs`
: A free-form attribute set.
···
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.
+14
nixos/doc/manual/from_md/development/option-types.section.xml
···
</varlistentry>
<varlistentry>
<term>
<literal>types.attrs</literal>
</term>
<listitem>
···
</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>