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 {
name = "int";
description = "signed integer";
check = isInt;
merge = mergeEqualOption;
};
name = "int";
description = "signed integer";
check = isInt;
merge = mergeEqualOption;
};
# Specialized subdomains of int
ints =
@ -292,10 +292,34 @@ rec {
port = ints.u16;
float = mkOptionType {
name = "float";
description = "floating point number";
check = isFloat;
merge = mergeEqualOption;
name = "float";
description = "floating point number";
check = isFloat;
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 {

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
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
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
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`
: A type that accepts any value and recursively merges attribute sets
@ -95,7 +100,7 @@ merging is handled.
problems.
:::
Integer-related types:
### Numeric types {#sec-option-types-numeric}
`types.int`
@ -118,6 +123,10 @@ Integer-related types:
from 0 to 2^n1 respectively (e.g. `0`
to `255` for 8 bits).
`types.ints.between` *`lowest highest`*
: An integer between *`lowest`* and *`highest`* (both inclusive).
`types.ints.positive`
: A positive integer (that is > 0).
@ -127,12 +136,44 @@ Integer-related types:
: A port number. This type is an alias to
`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`
: A string. Multiple definitions cannot be merged.
`types.separatedString` *`sep`*
: A string. Multiple definitions are concatenated with *`sep`*, e.g.
`types.separatedString "|"`.
`types.lines`
: A string. Multiple definitions are concatenated with a new line
@ -144,7 +185,7 @@ String-related types:
`types.envVar`
: A string. Multiple definitions are concatenated with a collon `":"`.
: A string. Multiple definitions are concatenated with a colon `":"`.
`types.strMatching`
@ -152,24 +193,9 @@ String-related types:
definitions cannot be merged. The regular expression is processed
using `builtins.match`.
## Value Types {#sec-option-types-value}
## Submodule types {#sec-option-types-submodule}
Value types are types that take a value parameter.
`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`.
Submodules are detailed in [Submodule](#section-option-types-submodule).
`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
options. This is equivalent to
`types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }`.
Submodules are detailed in [Submodule](#section-option-types-submodule).
`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
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
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
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
creation includes some more complex topics such as submodule handling,

View File

@ -6,7 +6,7 @@
in case of multiple value definitions.
</para>
<section xml:id="sec-option-types-basic">
<title>Basic Types</title>
<title>Basic types</title>
<para>
Basic types are the simplest available types in the module system.
Basic types include multiple string types that mainly differ in
@ -49,6 +49,20 @@
</para>
</listitem>
</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>
<term>
<literal>types.anything</literal>
@ -150,186 +164,241 @@
</listitem>
</varlistentry>
</variablelist>
<para>
Integer-related types:
</para>
<variablelist>
<varlistentry>
<term>
<literal>types.int</literal>
</term>
<listitem>
<para>
A signed integer.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.{s8, s16, s32}</literal>
</term>
<listitem>
<para>
Signed integers with a fixed length (8, 16 or 32 bits). They
go from 2^n/2 to 2^n/21 respectively (e.g.
<literal>128</literal> to <literal>127</literal> for 8
bits).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.unsigned</literal>
</term>
<listitem>
<para>
An unsigned integer (that is &gt;= 0).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.{u8, u16, u32}</literal>
</term>
<listitem>
<para>
Unsigned integers with a fixed length (8, 16 or 32 bits).
They go from 0 to 2^n1 respectively (e.g.
<literal>0</literal> to <literal>255</literal> for 8 bits).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.positive</literal>
</term>
<listitem>
<para>
A positive integer (that is &gt; 0).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.port</literal>
</term>
<listitem>
<para>
A port number. This type is an alias to
<literal>types.ints.u16</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
String-related types:
</para>
<variablelist>
<varlistentry>
<term>
<literal>types.str</literal>
</term>
<listitem>
<para>
A string. Multiple definitions cannot be merged.
</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
collon <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 xml:id="sec-option-types-numeric">
<title>Numeric types</title>
<variablelist>
<varlistentry>
<term>
<literal>types.int</literal>
</term>
<listitem>
<para>
A signed integer.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.{s8, s16, s32}</literal>
</term>
<listitem>
<para>
Signed integers with a fixed length (8, 16 or 32 bits).
They go from 2^n/2 to 2^n/21 respectively (e.g.
<literal>128</literal> to <literal>127</literal> for 8
bits).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.unsigned</literal>
</term>
<listitem>
<para>
An unsigned integer (that is &gt;= 0).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.{u8, u16, u32}</literal>
</term>
<listitem>
<para>
Unsigned integers with a fixed length (8, 16 or 32 bits).
They go from 0 to 2^n1 respectively (e.g.
<literal>0</literal> to <literal>255</literal> for 8
bits).
</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).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.ints.positive</literal>
</term>
<listitem>
<para>
A positive integer (that is &gt; 0).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.port</literal>
</term>
<listitem>
<para>
A port number. This type is an alias to
<literal>types.ints.u16</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.float</literal>
</term>
<listitem>
<para>
A floating point number.
</para>
<warning>
<para>
Converting a floating point number to a string with
<literal>toString</literal> or <literal>toJSON</literal>
may result in
<link xlink:href="https://github.com/NixOS/nix/issues/5733">precision
loss</link>.
</para>
</warning>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.number</literal>
</term>
<listitem>
<para>
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.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.numbers.between</literal>
<emphasis><literal>lowest highest</literal></emphasis>
</term>
<listitem>
<para>
An integer or floating point number between
<emphasis><literal>lowest</literal></emphasis> and
<emphasis><literal>highest</literal></emphasis> (both
inclusive).
</para>
</listitem>
</varlistentry>
<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 xml:id="sec-option-types-value">
<title>Value Types</title>
<section xml:id="sec-option-types-submodule">
<title>Submodule types</title>
<para>
Value types are types that take a value parameter.
Submodules are detailed in
<link linkend="section-option-types-submodule">Submodule</link>.
</para>
<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>
<term>
<literal>types.submodule</literal>
@ -345,8 +414,6 @@
in composed types to create modular options. This is
equivalent to
<literal>types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }</literal>.
Submodules are detailed in
<link linkend="section-option-types-submodule">Submodule</link>.
</para>
</listitem>
</varlistentry>
@ -467,7 +534,7 @@
</variablelist>
</section>
<section xml:id="sec-option-types-composed">
<title>Composed Types</title>
<title>Composed types</title>
<para>
Composed types are types that take a type as parameter.
<literal>listOf int</literal> and
@ -850,7 +917,7 @@ nixThings = mkOption {
</variablelist>
</section>
<section xml:id="sec-option-types-custom">
<title>Custom Types</title>
<title>Custom types</title>
<para>
Custom types can be created with the
<literal>mkOptionType</literal> function. As type creation

View File

@ -11,15 +11,6 @@ let
addCheck (listOf x) (y: length y == 2)
// { 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);
# Basically a tinkered lib.generators.mkKeyValueDefault
@ -93,7 +84,7 @@ in {
};
fadeSteps = mkOption {
type = pairOf (floatBetween 0.01 1);
type = pairOf (types.numbers.between 0.01 1);
default = [ 0.028 0.03 ];
example = [ 0.04 0.04 ];
description = lib.mdDoc ''
@ -133,7 +124,7 @@ in {
};
shadowOpacity = mkOption {
type = floatBetween 0 1;
type = types.numbers.between 0 1;
default = 0.75;
example = 0.8;
description = lib.mdDoc ''
@ -156,7 +147,7 @@ in {
};
activeOpacity = mkOption {
type = floatBetween 0 1;
type = types.numbers.between 0 1;
default = 1.0;
example = 0.8;
description = lib.mdDoc ''
@ -165,7 +156,7 @@ in {
};
inactiveOpacity = mkOption {
type = floatBetween 0.1 1;
type = types.numbers.between 0.1 1;
default = 1.0;
example = 0.8;
description = lib.mdDoc ''
@ -174,7 +165,7 @@ in {
};
menuOpacity = mkOption {
type = floatBetween 0 1;
type = types.numbers.between 0 1;
default = 1.0;
example = 0.8;
description = lib.mdDoc ''