Merge pull request #181834 from ncfavier/numbers

lib/types: add `number`
This commit is contained in:
Silvan Mosberger 2022-09-09 19:59:29 +02:00 committed by GitHub
commit 6389a26e5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 335 additions and 228 deletions

View File

@ -227,11 +227,11 @@ rec {
}; };
int = mkOptionType { int = mkOptionType {
name = "int"; name = "int";
description = "signed integer"; description = "signed integer";
check = isInt; check = isInt;
merge = mergeEqualOption; merge = mergeEqualOption;
}; };
# Specialized subdomains of int # Specialized subdomains of int
ints = ints =
@ -292,10 +292,34 @@ rec {
port = ints.u16; port = ints.u16;
float = mkOptionType { float = mkOptionType {
name = "float"; name = "float";
description = "floating point number"; description = "floating point number";
check = isFloat; check = isFloat;
merge = mergeEqualOption; merge = mergeEqualOption;
};
number = either int float;
numbers = let
betweenDesc = lowest: highest:
"${builtins.toJSON lowest} and ${builtins.toJSON highest} (both inclusive)";
in {
between = lowest: highest:
assert lib.assertMsg (lowest <= highest)
"numbers.between: lowest must be smaller than highest";
addCheck number (x: x >= lowest && x <= highest) // {
name = "numberBetween";
description = "integer or floating point number between ${betweenDesc lowest highest}";
};
nonnegative = addCheck number (x: x >= 0) // {
name = "numberNonnegative";
description = "nonnegative integer or floating point number, meaning >=0";
};
positive = addCheck number (x: x > 0) // {
name = "numberPositive";
description = "positive integer or floating point number, meaning >0";
};
}; };
str = mkOptionType { str = mkOptionType {

View File

@ -4,7 +4,7 @@ Option types are a way to put constraints on the values a module option
can take. Types are also responsible of how values are merged in case of can take. Types are also responsible of how values are merged in case of
multiple value definitions. multiple value definitions.
## Basic Types {#sec-option-types-basic} ## Basic types {#sec-option-types-basic}
Basic types are the simplest available types in the module system. Basic Basic types are the simplest available types in the module system. Basic
types include multiple string types that mainly differ in how definition types include multiple string types that mainly differ in how definition
@ -25,6 +25,11 @@ merging is handled.
: A top-level store path. This can be an attribute set pointing : A top-level store path. This can be an attribute set pointing
to a store path, like a derivation or a flake input. to a store path, like a derivation or a flake input.
`types.enum` *`l`*
: One element of the list *`l`*, e.g. `types.enum [ "left" "right" ]`.
Multiple definitions cannot be merged.
`types.anything` `types.anything`
: A type that accepts any value and recursively merges attribute sets : A type that accepts any value and recursively merges attribute sets
@ -95,7 +100,7 @@ merging is handled.
problems. problems.
::: :::
Integer-related types: ### Numeric types {#sec-option-types-numeric}
`types.int` `types.int`
@ -118,6 +123,10 @@ Integer-related types:
from 0 to 2^n1 respectively (e.g. `0` from 0 to 2^n1 respectively (e.g. `0`
to `255` for 8 bits). to `255` for 8 bits).
`types.ints.between` *`lowest highest`*
: An integer between *`lowest`* and *`highest`* (both inclusive).
`types.ints.positive` `types.ints.positive`
: A positive integer (that is > 0). : A positive integer (that is > 0).
@ -127,12 +136,44 @@ Integer-related types:
: A port number. This type is an alias to : A port number. This type is an alias to
`types.ints.u16`. `types.ints.u16`.
String-related types: `types.float`
: A floating point number.
::: {.warning}
Converting a floating point number to a string with `toString` or `toJSON`
may result in [precision loss](https://github.com/NixOS/nix/issues/5733).
:::
`types.number`
: Either a signed integer or a floating point number. No implicit conversion
is done between the two types, and multiple equal definitions will only be
merged if they have the same type.
`types.numbers.between` *`lowest highest`*
: An integer or floating point number between *`lowest`* and *`highest`* (both inclusive).
`types.numbers.nonnegative`
: A nonnegative integer or floating point number (that is >= 0).
`types.numbers.positive`
: A positive integer or floating point number (that is > 0).
### String types {#sec-option-types-string}
`types.str` `types.str`
: A string. Multiple definitions cannot be merged. : A string. Multiple definitions cannot be merged.
`types.separatedString` *`sep`*
: A string. Multiple definitions are concatenated with *`sep`*, e.g.
`types.separatedString "|"`.
`types.lines` `types.lines`
: A string. Multiple definitions are concatenated with a new line : A string. Multiple definitions are concatenated with a new line
@ -144,7 +185,7 @@ String-related types:
`types.envVar` `types.envVar`
: A string. Multiple definitions are concatenated with a collon `":"`. : A string. Multiple definitions are concatenated with a colon `":"`.
`types.strMatching` `types.strMatching`
@ -152,24 +193,9 @@ String-related types:
definitions cannot be merged. The regular expression is processed definitions cannot be merged. The regular expression is processed
using `builtins.match`. using `builtins.match`.
## Value Types {#sec-option-types-value} ## Submodule types {#sec-option-types-submodule}
Value types are types that take a value parameter. Submodules are detailed in [Submodule](#section-option-types-submodule).
`types.enum` *`l`*
: One element of the list *`l`*, e.g. `types.enum [ "left" "right" ]`.
Multiple definitions cannot be merged.
`types.separatedString` *`sep`*
: A string with a custom separator *`sep`*, e.g.
`types.separatedString "|"`.
`types.ints.between` *`lowest highest`*
: An integer between *`lowest`* and *`highest`* (both inclusive). Useful
for creating types like `types.port`.
`types.submodule` *`o`* `types.submodule` *`o`*
@ -178,7 +204,6 @@ Value types are types that take a value parameter.
value. Submodules are used in composed types to create modular value. Submodules are used in composed types to create modular
options. This is equivalent to options. This is equivalent to
`types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }`. `types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }`.
Submodules are detailed in [Submodule](#section-option-types-submodule).
`types.submoduleWith` { *`modules`*, *`specialArgs`* ? {}, *`shorthandOnlyDefinesConfig`* ? false } `types.submoduleWith` { *`modules`*, *`specialArgs`* ? {}, *`shorthandOnlyDefinesConfig`* ? false }
@ -239,7 +264,7 @@ Value types are types that take a value parameter.
more convenient and discoverable than expecting the module user to more convenient and discoverable than expecting the module user to
type-merge with the `attrsOf submodule` option. type-merge with the `attrsOf submodule` option.
## Composed Types {#sec-option-types-composed} ## Composed types {#sec-option-types-composed}
Composed types are types that take a type as parameter. `listOf Composed types are types that take a type as parameter. `listOf
int` and `either int str` are examples of composed types. int` and `either int str` are examples of composed types.
@ -496,7 +521,7 @@ Types are mainly characterized by their `check` and `merge` functions.
of strings, and `defs` the list of defined values as a list. It is of strings, and `defs` the list of defined values as a list. It is
possible to override a type merge function for custom needs. possible to override a type merge function for custom needs.
## Custom Types {#sec-option-types-custom} ## Custom types {#sec-option-types-custom}
Custom types can be created with the `mkOptionType` function. As type Custom types can be created with the `mkOptionType` function. As type
creation includes some more complex topics such as submodule handling, creation includes some more complex topics such as submodule handling,

View File

@ -6,7 +6,7 @@
in case of multiple value definitions. in case of multiple value definitions.
</para> </para>
<section xml:id="sec-option-types-basic"> <section xml:id="sec-option-types-basic">
<title>Basic Types</title> <title>Basic types</title>
<para> <para>
Basic types are the simplest available types in the module system. Basic types are the simplest available types in the module system.
Basic types include multiple string types that mainly differ in Basic types include multiple string types that mainly differ in
@ -49,6 +49,20 @@
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<literal>types.enum</literal>
<emphasis><literal>l</literal></emphasis>
</term>
<listitem>
<para>
One element of the list
<emphasis><literal>l</literal></emphasis>, e.g.
<literal>types.enum [ &quot;left&quot; &quot;right&quot; ]</literal>.
Multiple definitions cannot be merged.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<literal>types.anything</literal> <literal>types.anything</literal>
@ -150,186 +164,241 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<para> <section xml:id="sec-option-types-numeric">
Integer-related types: <title>Numeric types</title>
</para> <variablelist>
<variablelist> <varlistentry>
<varlistentry> <term>
<term> <literal>types.int</literal>
<literal>types.int</literal> </term>
</term> <listitem>
<listitem> <para>
<para> A signed integer.
A signed integer. </para>
</para> </listitem>
</listitem> </varlistentry>
</varlistentry> <varlistentry>
<varlistentry> <term>
<term> <literal>types.ints.{s8, s16, s32}</literal>
<literal>types.ints.{s8, s16, s32}</literal> </term>
</term> <listitem>
<listitem> <para>
<para> Signed integers with a fixed length (8, 16 or 32 bits).
Signed integers with a fixed length (8, 16 or 32 bits). They They go from 2^n/2 to 2^n/21 respectively (e.g.
go from 2^n/2 to 2^n/21 respectively (e.g. <literal>128</literal> to <literal>127</literal> for 8
<literal>128</literal> to <literal>127</literal> for 8 bits).
bits). </para>
</para> </listitem>
</listitem> </varlistentry>
</varlistentry> <varlistentry>
<varlistentry> <term>
<term> <literal>types.ints.unsigned</literal>
<literal>types.ints.unsigned</literal> </term>
</term> <listitem>
<listitem> <para>
<para> An unsigned integer (that is &gt;= 0).
An unsigned integer (that is &gt;= 0). </para>
</para> </listitem>
</listitem> </varlistentry>
</varlistentry> <varlistentry>
<varlistentry> <term>
<term> <literal>types.ints.{u8, u16, u32}</literal>
<literal>types.ints.{u8, u16, u32}</literal> </term>
</term> <listitem>
<listitem> <para>
<para> Unsigned integers with a fixed length (8, 16 or 32 bits).
Unsigned integers with a fixed length (8, 16 or 32 bits). They go from 0 to 2^n1 respectively (e.g.
They go from 0 to 2^n1 respectively (e.g. <literal>0</literal> to <literal>255</literal> for 8
<literal>0</literal> to <literal>255</literal> for 8 bits). bits).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<literal>types.ints.positive</literal> <literal>types.ints.between</literal>
</term> <emphasis><literal>lowest highest</literal></emphasis>
<listitem> </term>
<para> <listitem>
A positive integer (that is &gt; 0). <para>
</para> An integer between
</listitem> <emphasis><literal>lowest</literal></emphasis> and
</varlistentry> <emphasis><literal>highest</literal></emphasis> (both
<varlistentry> inclusive).
<term> </para>
<literal>types.port</literal> </listitem>
</term> </varlistentry>
<listitem> <varlistentry>
<para> <term>
A port number. This type is an alias to <literal>types.ints.positive</literal>
<literal>types.ints.u16</literal>. </term>
</para> <listitem>
</listitem> <para>
</varlistentry> A positive integer (that is &gt; 0).
</variablelist> </para>
<para> </listitem>
String-related types: </varlistentry>
</para> <varlistentry>
<variablelist> <term>
<varlistentry> <literal>types.port</literal>
<term> </term>
<literal>types.str</literal> <listitem>
</term> <para>
<listitem> A port number. This type is an alias to
<para> <literal>types.ints.u16</literal>.
A string. Multiple definitions cannot be merged. </para>
</para> </listitem>
</listitem> </varlistentry>
</varlistentry> <varlistentry>
<varlistentry> <term>
<term> <literal>types.float</literal>
<literal>types.lines</literal> </term>
</term> <listitem>
<listitem> <para>
<para> A floating point number.
A string. Multiple definitions are concatenated with a new </para>
line <literal>&quot;\n&quot;</literal>. <warning>
</para> <para>
</listitem> Converting a floating point number to a string with
</varlistentry> <literal>toString</literal> or <literal>toJSON</literal>
<varlistentry> may result in
<term> <link xlink:href="https://github.com/NixOS/nix/issues/5733">precision
<literal>types.commas</literal> loss</link>.
</term> </para>
<listitem> </warning>
<para> </listitem>
A string. Multiple definitions are concatenated with a comma </varlistentry>
<literal>&quot;,&quot;</literal>. <varlistentry>
</para> <term>
</listitem> <literal>types.number</literal>
</varlistentry> </term>
<varlistentry> <listitem>
<term> <para>
<literal>types.envVar</literal> Either a signed integer or a floating point number. No
</term> implicit conversion is done between the two types, and
<listitem> multiple equal definitions will only be merged if they
<para> have the same type.
A string. Multiple definitions are concatenated with a </para>
collon <literal>&quot;:&quot;</literal>. </listitem>
</para> </varlistentry>
</listitem> <varlistentry>
</varlistentry> <term>
<varlistentry> <literal>types.numbers.between</literal>
<term> <emphasis><literal>lowest highest</literal></emphasis>
<literal>types.strMatching</literal> </term>
</term> <listitem>
<listitem> <para>
<para> An integer or floating point number between
A string matching a specific regular expression. Multiple <emphasis><literal>lowest</literal></emphasis> and
definitions cannot be merged. The regular expression is <emphasis><literal>highest</literal></emphasis> (both
processed using <literal>builtins.match</literal>. inclusive).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> <varlistentry>
<term>
<literal>types.numbers.nonnegative</literal>
</term>
<listitem>
<para>
A nonnegative integer or floating point number (that is
&gt;= 0).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.numbers.positive</literal>
</term>
<listitem>
<para>
A positive integer or floating point number (that is &gt;
0).
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="sec-option-types-string">
<title>String types</title>
<variablelist>
<varlistentry>
<term>
<literal>types.str</literal>
</term>
<listitem>
<para>
A string. Multiple definitions cannot be merged.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.separatedString</literal>
<emphasis><literal>sep</literal></emphasis>
</term>
<listitem>
<para>
A string. Multiple definitions are concatenated with
<emphasis><literal>sep</literal></emphasis>, e.g.
<literal>types.separatedString &quot;|&quot;</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.lines</literal>
</term>
<listitem>
<para>
A string. Multiple definitions are concatenated with a new
line <literal>&quot;\n&quot;</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.commas</literal>
</term>
<listitem>
<para>
A string. Multiple definitions are concatenated with a
comma <literal>&quot;,&quot;</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.envVar</literal>
</term>
<listitem>
<para>
A string. Multiple definitions are concatenated with a
colon <literal>&quot;:&quot;</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.strMatching</literal>
</term>
<listitem>
<para>
A string matching a specific regular expression. Multiple
definitions cannot be merged. The regular expression is
processed using <literal>builtins.match</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
</section> </section>
<section xml:id="sec-option-types-value"> <section xml:id="sec-option-types-submodule">
<title>Value Types</title> <title>Submodule types</title>
<para> <para>
Value types are types that take a value parameter. Submodules are detailed in
<link linkend="section-option-types-submodule">Submodule</link>.
</para> </para>
<variablelist> <variablelist>
<varlistentry>
<term>
<literal>types.enum</literal>
<emphasis><literal>l</literal></emphasis>
</term>
<listitem>
<para>
One element of the list
<emphasis><literal>l</literal></emphasis>, e.g.
<literal>types.enum [ &quot;left&quot; &quot;right&quot; ]</literal>.
Multiple definitions cannot be merged.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.separatedString</literal>
<emphasis><literal>sep</literal></emphasis>
</term>
<listitem>
<para>
A string with a custom separator
<emphasis><literal>sep</literal></emphasis>, e.g.
<literal>types.separatedString &quot;|&quot;</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.between</literal>
<emphasis><literal>lowest highest</literal></emphasis>
</term>
<listitem>
<para>
An integer between
<emphasis><literal>lowest</literal></emphasis> and
<emphasis><literal>highest</literal></emphasis> (both
inclusive). Useful for creating types like
<literal>types.port</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<literal>types.submodule</literal> <literal>types.submodule</literal>
@ -345,8 +414,6 @@
in composed types to create modular options. This is in composed types to create modular options. This is
equivalent to equivalent to
<literal>types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }</literal>. <literal>types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }</literal>.
Submodules are detailed in
<link linkend="section-option-types-submodule">Submodule</link>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -467,7 +534,7 @@
</variablelist> </variablelist>
</section> </section>
<section xml:id="sec-option-types-composed"> <section xml:id="sec-option-types-composed">
<title>Composed Types</title> <title>Composed types</title>
<para> <para>
Composed types are types that take a type as parameter. Composed types are types that take a type as parameter.
<literal>listOf int</literal> and <literal>listOf int</literal> and
@ -850,7 +917,7 @@ nixThings = mkOption {
</variablelist> </variablelist>
</section> </section>
<section xml:id="sec-option-types-custom"> <section xml:id="sec-option-types-custom">
<title>Custom Types</title> <title>Custom types</title>
<para> <para>
Custom types can be created with the Custom types can be created with the
<literal>mkOptionType</literal> function. As type creation <literal>mkOptionType</literal> function. As type creation

View File

@ -11,15 +11,6 @@ let
addCheck (listOf x) (y: length y == 2) addCheck (listOf x) (y: length y == 2)
// { description = "pair of ${x.description}"; }; // { description = "pair of ${x.description}"; };
floatBetween = a: b: with types;
let
# toString prints floats with hardcoded high precision
floatToString = f: builtins.toJSON f;
in
addCheck float (x: x <= b && x >= a)
// { description = "a floating point number in " +
"range [${floatToString a}, ${floatToString b}]"; };
mkDefaultAttrs = mapAttrs (n: v: mkDefault v); mkDefaultAttrs = mapAttrs (n: v: mkDefault v);
# Basically a tinkered lib.generators.mkKeyValueDefault # Basically a tinkered lib.generators.mkKeyValueDefault
@ -93,7 +84,7 @@ in {
}; };
fadeSteps = mkOption { fadeSteps = mkOption {
type = pairOf (floatBetween 0.01 1); type = pairOf (types.numbers.between 0.01 1);
default = [ 0.028 0.03 ]; default = [ 0.028 0.03 ];
example = [ 0.04 0.04 ]; example = [ 0.04 0.04 ];
description = lib.mdDoc '' description = lib.mdDoc ''
@ -133,7 +124,7 @@ in {
}; };
shadowOpacity = mkOption { shadowOpacity = mkOption {
type = floatBetween 0 1; type = types.numbers.between 0 1;
default = 0.75; default = 0.75;
example = 0.8; example = 0.8;
description = lib.mdDoc '' description = lib.mdDoc ''
@ -156,7 +147,7 @@ in {
}; };
activeOpacity = mkOption { activeOpacity = mkOption {
type = floatBetween 0 1; type = types.numbers.between 0 1;
default = 1.0; default = 1.0;
example = 0.8; example = 0.8;
description = lib.mdDoc '' description = lib.mdDoc ''
@ -165,7 +156,7 @@ in {
}; };
inactiveOpacity = mkOption { inactiveOpacity = mkOption {
type = floatBetween 0.1 1; type = types.numbers.between 0.1 1;
default = 1.0; default = 1.0;
example = 0.8; example = 0.8;
description = lib.mdDoc '' description = lib.mdDoc ''
@ -174,7 +165,7 @@ in {
}; };
menuOpacity = mkOption { menuOpacity = mkOption {
type = floatBetween 0 1; type = types.numbers.between 0 1;
default = 1.0; default = 1.0;
example = 0.8; example = 0.8;
description = lib.mdDoc '' description = lib.mdDoc ''