forked from mirrors/nixpkgs
Merge pull request #97119 from Infinisil/types.anything
Introduce `types.anything`
This commit is contained in:
commit
f3893d8b53
|
@ -107,6 +107,10 @@ rec {
|
||||||
/* "Merge" option definitions by checking that they all have the same value. */
|
/* "Merge" option definitions by checking that they all have the same value. */
|
||||||
mergeEqualOption = loc: defs:
|
mergeEqualOption = loc: defs:
|
||||||
if defs == [] then abort "This case should never happen."
|
if defs == [] then abort "This case should never happen."
|
||||||
|
# Return early if we only have one element
|
||||||
|
# This also makes it work for functions, because the foldl' below would try
|
||||||
|
# to compare the first element with itself, which is false for functions
|
||||||
|
else if length defs == 1 then (elemAt defs 0).value
|
||||||
else foldl' (val: def:
|
else foldl' (val: def:
|
||||||
if def.value != val then
|
if def.value != val then
|
||||||
throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}."
|
throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}."
|
||||||
|
|
|
@ -233,6 +233,35 @@ checkConfigError 'infinite recursion encountered' config.foo ./freeform-attrsOf.
|
||||||
checkConfigError 'The option .* is used but not defined' config.foo ./freeform-lazyAttrsOf.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
|
checkConfigOutput 24 config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix ./define-value-string.nix
|
||||||
|
|
||||||
|
## types.anything
|
||||||
|
# Check that attribute sets are merged recursively
|
||||||
|
checkConfigOutput null config.value.foo ./types-anything/nested-attrs.nix
|
||||||
|
checkConfigOutput null config.value.l1.foo ./types-anything/nested-attrs.nix
|
||||||
|
checkConfigOutput null config.value.l1.l2.foo ./types-anything/nested-attrs.nix
|
||||||
|
checkConfigOutput null config.value.l1.l2.l3.foo ./types-anything/nested-attrs.nix
|
||||||
|
# Attribute sets that are coercible to strings shouldn't be recursed into
|
||||||
|
checkConfigOutput foo config.value.outPath ./types-anything/attrs-coercible.nix
|
||||||
|
# Multiple lists aren't concatenated together
|
||||||
|
checkConfigError 'The option .* has conflicting definitions' config.value ./types-anything/lists.nix
|
||||||
|
# Check that all equalizable atoms can be used as long as all definitions are equal
|
||||||
|
checkConfigOutput 0 config.value.int ./types-anything/equal-atoms.nix
|
||||||
|
checkConfigOutput false config.value.bool ./types-anything/equal-atoms.nix
|
||||||
|
checkConfigOutput '""' config.value.string ./types-anything/equal-atoms.nix
|
||||||
|
checkConfigOutput / config.value.path ./types-anything/equal-atoms.nix
|
||||||
|
checkConfigOutput null config.value.null ./types-anything/equal-atoms.nix
|
||||||
|
checkConfigOutput 0.1 config.value.float ./types-anything/equal-atoms.nix
|
||||||
|
# Functions can't be merged together
|
||||||
|
checkConfigError "The option .* has conflicting definitions" config.value.multiple-lambdas ./types-anything/functions.nix
|
||||||
|
checkConfigOutput '<LAMBDA>' config.value.single-lambda ./types-anything/functions.nix
|
||||||
|
# Check that all mk* modifiers are applied
|
||||||
|
checkConfigError 'attribute .* not found' config.value.mkiffalse ./types-anything/mk-mods.nix
|
||||||
|
checkConfigOutput '{ }' config.value.mkiftrue ./types-anything/mk-mods.nix
|
||||||
|
checkConfigOutput 1 config.value.mkdefault ./types-anything/mk-mods.nix
|
||||||
|
checkConfigOutput '{ }' config.value.mkmerge ./types-anything/mk-mods.nix
|
||||||
|
checkConfigOutput true config.value.mkbefore ./types-anything/mk-mods.nix
|
||||||
|
checkConfigOutput 1 config.value.nested.foo ./types-anything/mk-mods.nix
|
||||||
|
checkConfigOutput baz config.value.nested.bar.baz ./types-anything/mk-mods.nix
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
====== module tests ======
|
====== module tests ======
|
||||||
$pass Pass
|
$pass Pass
|
||||||
|
|
12
lib/tests/modules/types-anything/attrs-coercible.nix
Normal file
12
lib/tests/modules/types-anything/attrs-coercible.nix
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{ lib, ... }: {
|
||||||
|
|
||||||
|
options.value = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
};
|
||||||
|
|
||||||
|
config.value = {
|
||||||
|
outPath = "foo";
|
||||||
|
err = throw "err";
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
lib/tests/modules/types-anything/equal-atoms.nix
Normal file
26
lib/tests/modules/types-anything/equal-atoms.nix
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{ lib, ... }: {
|
||||||
|
|
||||||
|
options.value = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
value.int = 0;
|
||||||
|
value.bool = false;
|
||||||
|
value.string = "";
|
||||||
|
value.path = /.;
|
||||||
|
value.null = null;
|
||||||
|
value.float = 0.1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.int = 0;
|
||||||
|
value.bool = false;
|
||||||
|
value.string = "";
|
||||||
|
value.path = /.;
|
||||||
|
value.null = null;
|
||||||
|
value.float = 0.1;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
17
lib/tests/modules/types-anything/functions.nix
Normal file
17
lib/tests/modules/types-anything/functions.nix
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{ lib, ... }: {
|
||||||
|
|
||||||
|
options.value = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
value.single-lambda = x: x;
|
||||||
|
value.multiple-lambdas = x: x;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.multiple-lambdas = x: x;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
16
lib/tests/modules/types-anything/lists.nix
Normal file
16
lib/tests/modules/types-anything/lists.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{ lib, ... }: {
|
||||||
|
|
||||||
|
options.value = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
value = [ null ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value = [ null ];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
44
lib/tests/modules/types-anything/mk-mods.nix
Normal file
44
lib/tests/modules/types-anything/mk-mods.nix
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{ lib, ... }: {
|
||||||
|
|
||||||
|
options.value = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
value.mkiffalse = lib.mkIf false {};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.mkiftrue = lib.mkIf true {};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.mkdefault = lib.mkDefault 0;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.mkdefault = 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.mkmerge = lib.mkMerge [
|
||||||
|
{}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.mkbefore = lib.mkBefore true;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.nested = lib.mkMerge [
|
||||||
|
{
|
||||||
|
foo = lib.mkDefault 0;
|
||||||
|
bar = lib.mkIf false 0;
|
||||||
|
}
|
||||||
|
(lib.mkIf true {
|
||||||
|
foo = lib.mkIf true (lib.mkForce 1);
|
||||||
|
bar = {
|
||||||
|
baz = lib.mkDefault "baz";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
22
lib/tests/modules/types-anything/nested-attrs.nix
Normal file
22
lib/tests/modules/types-anything/nested-attrs.nix
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{ lib, ... }: {
|
||||||
|
|
||||||
|
options.value = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
value.foo = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.l1.foo = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.l1.l2.foo = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.l1.l2.l3.foo = null;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
|
@ -104,6 +104,42 @@ rec {
|
||||||
# When adding new types don't forget to document them in
|
# When adding new types don't forget to document them in
|
||||||
# nixos/doc/manual/development/option-types.xml!
|
# nixos/doc/manual/development/option-types.xml!
|
||||||
types = rec {
|
types = rec {
|
||||||
|
|
||||||
|
anything = mkOptionType {
|
||||||
|
name = "anything";
|
||||||
|
description = "anything";
|
||||||
|
check = value: true;
|
||||||
|
merge = loc: defs:
|
||||||
|
let
|
||||||
|
getType = value:
|
||||||
|
if isAttrs value && isCoercibleToString value
|
||||||
|
then "stringCoercibleSet"
|
||||||
|
else builtins.typeOf value;
|
||||||
|
|
||||||
|
# Returns the common type of all definitions, throws an error if they
|
||||||
|
# don't have the same type
|
||||||
|
commonType = foldl' (type: def:
|
||||||
|
if getType def.value == type
|
||||||
|
then type
|
||||||
|
else throw "The option `${showOption loc}' has conflicting option types in ${showFiles (getFiles defs)}"
|
||||||
|
) (getType (head defs).value) defs;
|
||||||
|
|
||||||
|
mergeFunction = {
|
||||||
|
# Recursively merge attribute sets
|
||||||
|
set = (attrsOf anything).merge;
|
||||||
|
# Safe and deterministic behavior for lists is to only accept one definition
|
||||||
|
# listOf only used to apply mkIf and co.
|
||||||
|
list =
|
||||||
|
if length defs > 1
|
||||||
|
then throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}."
|
||||||
|
else (listOf anything).merge;
|
||||||
|
# This is the type of packages, only accept a single definition
|
||||||
|
stringCoercibleSet = mergeOneOption;
|
||||||
|
# Otherwise fall back to only allowing all equal definitions
|
||||||
|
}.${commonType} or mergeEqualOption;
|
||||||
|
in mergeFunction loc defs;
|
||||||
|
};
|
||||||
|
|
||||||
unspecified = mkOptionType {
|
unspecified = mkOptionType {
|
||||||
name = "unspecified";
|
name = "unspecified";
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,16 +21,6 @@
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
<varname>types.attrs</varname>
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
A free-form attribute set.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<varname>types.bool</varname>
|
<varname>types.bool</varname>
|
||||||
|
@ -64,6 +54,64 @@
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<varname>types.anything</varname>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A type that accepts any value and recursively merges attribute sets together.
|
||||||
|
This type is recommended when the option type is unknown.
|
||||||
|
<example xml:id="ex-types-anything">
|
||||||
|
<title><literal>types.anything</literal> Example</title>
|
||||||
|
<para>
|
||||||
|
Two definitions of this type like
|
||||||
|
<programlisting>
|
||||||
|
{
|
||||||
|
str = lib.mkDefault "foo";
|
||||||
|
pkg.hello = pkgs.hello;
|
||||||
|
fun.fun = x: x + 1;
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
<programlisting>
|
||||||
|
{
|
||||||
|
str = lib.mkIf true "bar";
|
||||||
|
pkg.gcc = pkgs.gcc;
|
||||||
|
fun.fun = lib.mkForce (x: x + 2);
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
will get merged to
|
||||||
|
<programlisting>
|
||||||
|
{
|
||||||
|
str = "bar";
|
||||||
|
pkg.gcc = pkgs.gcc;
|
||||||
|
pkg.hello = pkgs.hello;
|
||||||
|
fun.fun = x: x + 2;
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</example>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<varname>types.attrs</varname>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A free-form attribute set.
|
||||||
|
<warning><para>
|
||||||
|
This type will be deprecated in the future because it doesn't recurse
|
||||||
|
into attribute sets, silently drops earlier attribute definitions, and
|
||||||
|
doesn't discharge <literal>lib.mkDefault</literal>, <literal>lib.mkIf
|
||||||
|
</literal> and co. For allowing arbitrary attribute sets, prefer
|
||||||
|
<literal>types.attrsOf types.anything</literal> instead which doesn't
|
||||||
|
have these problems.
|
||||||
|
</para></warning>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
Loading…
Reference in a new issue