Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib/types: add signed/unsigned integer types #27965

Merged
merged 12 commits into from Nov 5, 2017
2 changes: 1 addition & 1 deletion lib/modules.nix
Expand Up @@ -338,7 +338,7 @@ rec {
# Type-check the remaining definitions, and merge them.
mergedValue = foldl' (res: def:
if type.check def.value then res
else throw "The option value `${showOption loc}' in `${def.file}' is not a ${type.description}.")
else throw "The option value `${showOption loc}' in `${def.file}' is not of type `${type.description}'.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I understand we are moving away from asymmetrical quoting?
This set of commit changes the quoting within comments as well, please revert to do only one type of quotation mark.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are? My impression was Eelco would like to keep the “special” quoting?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12:03:46        niksnut | using `...' is always wrong

heh. :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unicode quotes have practical implications that make them undesirable.

See NixOS/rfcs#4

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

` is not unicode, though.

(type.merge loc defsFinal) defsFinal;

isDefined = defsFinal != [];
Expand Down
12 changes: 11 additions & 1 deletion lib/tests/modules.sh
Expand Up @@ -61,6 +61,16 @@ checkConfigError() {
checkConfigOutput "false" config.enable ./declare-enable.nix
checkConfigError 'The option .* defined in .* does not exist.' config.enable ./define-enable.nix

# Check integer types.
# unsigned
checkConfigOutput "42" config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix
checkConfigError 'The option value .* in .* is not of type.*unsigned integer.*' config.value ./declare-int-unsigned-value.nix ./define-value-int-negative.nix
# positive
checkConfigError 'The option value .* in .* is not of type.*positive integer.*' config.value ./declare-int-positive-value.nix ./define-value-int-zero.nix
# between
checkConfigOutput "42" config.value ./declare-int-between-value.nix ./define-value-int-positive.nix
checkConfigError 'The option value .* in .* is not of type.*between.*-21 and 43.*inclusive.*' config.value ./declare-int-between-value.nix ./define-value-int-negative.nix

# Check mkForce without submodules.
set -- config.enable ./declare-enable.nix ./define-enable.nix
checkConfigOutput "true" "$@"
Expand Down Expand Up @@ -126,7 +136,7 @@ checkConfigOutput "true" "$@" ./define-module-check.nix
# Check coerced value.
checkConfigOutput "\"42\"" config.value ./declare-coerced-value.nix
checkConfigOutput "\"24\"" config.value ./declare-coerced-value.nix ./define-value-string.nix
checkConfigError 'The option value .* in .* is not a string or integer.' config.value ./declare-coerced-value.nix ./define-value-list.nix
checkConfigError 'The option value .* in .* is not.*string or signed integer.*' config.value ./declare-coerced-value.nix ./define-value-list.nix

cat <<EOF
====== module tests ======
Expand Down
9 changes: 9 additions & 0 deletions lib/tests/modules/declare-int-between-value.nix
@@ -0,0 +1,9 @@
{ lib, ... }:

{
options = {
value = lib.mkOption {
type = lib.types.ints.between (-21) 43;
};
};
}
9 changes: 9 additions & 0 deletions lib/tests/modules/declare-int-positive-value.nix
@@ -0,0 +1,9 @@
{ lib, ... }:

{
options = {
value = lib.mkOption {
type = lib.types.ints.positive;
};
};
}
9 changes: 9 additions & 0 deletions lib/tests/modules/declare-int-unsigned-value.nix
@@ -0,0 +1,9 @@
{ lib, ... }:

{
options = {
value = lib.mkOption {
type = lib.types.ints.unsigned;
};
};
}
3 changes: 3 additions & 0 deletions lib/tests/modules/define-value-int-negative.nix
@@ -0,0 +1,3 @@
{
value = -23;
}
3 changes: 3 additions & 0 deletions lib/tests/modules/define-value-int-positive.nix
@@ -0,0 +1,3 @@
{
value = 42;
}
3 changes: 3 additions & 0 deletions lib/tests/modules/define-value-int-zero.nix
@@ -0,0 +1,3 @@
{
value = 0;
}
69 changes: 61 additions & 8 deletions lib/types.nix
Expand Up @@ -108,11 +108,64 @@ rec {
};

int = mkOptionType rec {
name = "int";
description = "integer";
check = isInt;
merge = mergeOneOption;
};
name = "int";
description = "signed integer";
check = isInt;
merge = mergeOneOption;
};

# Specialized subdomains of int
ints =
let
betweenDesc = lowest: highest:
"${toString lowest} and ${toString highest} (both inclusive)";
between = lowest: highest: assert lowest <= highest;
addCheck int (x: x >= lowest && x <= highest) // {
name = "intBetween";
description = "integer between ${betweenDesc lowest highest}";
};
ign = lowest: highest: name: docStart:
between lowest highest // {
inherit name;
description = docStart + "; between ${betweenDesc lowest highest}";
};
unsign = bit: range: ign 0 (range - 1)
"unsignedInt${toString bit}" "${toString bit} bit unsigned integer";
sign = bit: range: ign (0 - (range / 2)) (range / 2 - 1)
"signedInt${toString bit}" "${toString bit} bit signed integer";

in rec {
/* An int with a fixed range.
*
* Example:
* (ints.between 0 100).check (-1)
* => false
* (ints.between 0 100).check (101)
* => false
* (ints.between 0 0).check 0
* => true
*/
inherit between;

unsigned = addCheck types.int (x: x >= 0) // {
name = "unsignedInt";
description = "unsigned integer, meaning >=0";
};
positive = addCheck types.int (x: x > 0) // {
name = "positiveInt";
description = "positive integer, meaning >0";
};
u8 = unsign 8 256;
u16 = unsign 16 65536;
u32 = unsign 32 4294967296;
# the biggest int the nix lexer accepts is 9223372036854775808
# the smallest int the nix lexer accepts is -9223372036854775807
# unsigned64 = unsign 64 18446744073709551616;

s8 = sign 8 256;
s16 = sign 16 65536;
s32 = sign 32 4294967296;
};

str = mkOptionType {
name = "str";
Expand Down Expand Up @@ -172,7 +225,7 @@ rec {
};

# drop this in the future:
list = builtins.trace "`types.list' is deprecated; use `types.listOf' instead" types.listOf;
list = builtins.trace "`types.list` is deprecated; use `types.listOf` instead" types.listOf;

listOf = elemType: mkOptionType rec {
name = "listOf";
Expand All @@ -189,7 +242,7 @@ rec {
).optionalValue
) def.value
else
throw "The option value `${showOption loc}' in `${def.file}' is not a list.") defs)));
throw "The option value `${showOption loc}` in `${def.file}` is not a list.") defs)));
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m);
Expand Down Expand Up @@ -260,7 +313,7 @@ rec {
let nrNulls = count (def: def.value == null) defs; in
if nrNulls == length defs then null
else if nrNulls != 0 then
throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}."
throw "The option `${showOption loc}` is defined both null and not null, in ${showFiles (getFiles defs)}."
else elemType.merge loc defs;
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
Expand Down
71 changes: 65 additions & 6 deletions nixos/doc/manual/development/option-types.xml
Expand Up @@ -22,10 +22,6 @@
<listitem><para>A boolean, its values can be <literal>true</literal> or
<literal>false</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>types.int</varname></term>
<listitem><para>An integer.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>types.path</varname></term>
<listitem><para>A filesystem path, defined as anything that when coerced to
Expand All @@ -39,7 +35,59 @@
</varlistentry>
</variablelist>

<para>String related types:</para>
<para>Integer-related types:</para>

<variablelist>
<varlistentry>
<term><varname>types.int</varname></term>
<listitem><para>A signed integer.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<varname>types.ints.{s8, s16, s32}</varname>
</term>
<listitem>
<para>Signed integers with a fixed length (8, 16 or 32 bits).
They go from
<inlineequation><mathphrase>−2<superscript>n</superscript>/2</mathphrase>
</inlineequation> to <inlineequation>
<mathphrase>2<superscript>n</superscript>/2−1</mathphrase>
</inlineequation>
respectively (e.g. <literal>−128</literal> to <literal>127</literal>
for 8 bits).
</para></listitem>
</varlistentry>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind breaking these out, and specifying exactly their min/max?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if that’d improve the description, since those are based on byte-lengths anyway.

<varlistentry>
<term>
<varname>types.ints.unsigned</varname>
</term>
<listitem><para>An unsigned integer (that is >= 0).
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<varname>types.ints.{u8, u16, u32}</varname>
</term>
<listitem>
<para>Unsigned integers with a fixed length (8, 16 or 32 bits).
They go from
<inlineequation><mathphrase>0</mathphrase></inlineequation> to <inlineequation>
<mathphrase>2<superscript>n</superscript>−1</mathphrase>
</inlineequation>
respectively (e.g. <literal>0</literal> to <literal>255</literal>
for 8 bits).
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<varname>types.ints.positive</varname>
</term>
<listitem><para>A positive integer (that is > 0).
</para></listitem>
</varlistentry>
</variablelist>

<para>String-related types:</para>

<variablelist>
<varlistentry>
Expand Down Expand Up @@ -68,7 +116,7 @@

<section><title>Value Types</title>

<para>Value types are type that take a value parameter.</para>
<para>Value types are types that take a value parameter.</para>

<variablelist>
<varlistentry>
Expand All @@ -84,6 +132,17 @@
<replaceable>sep</replaceable>, e.g. <literal>types.separatedString
"|"</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<varname>types.ints.between</varname>
<replaceable>lowest</replaceable>
<replaceable>highest</replaceable>
</term>
<listitem><para>An integer between <replaceable>lowest</replaceable>
and <replaceable>highest</replaceable> (both inclusive).
Useful for creating types like <literal>types.port</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>types.submodule</varname> <replaceable>o</replaceable></term>
<listitem><para>A set of sub options <replaceable>o</replaceable>.
Expand Down