Loading lib/tests/modules.sh +24 −4 Original line number Diff line number Diff line Loading @@ -58,12 +58,19 @@ logFailure() { evalConfig() { local attr=$1 shift local script="import ./default.nix { modules = [ $* ];}" local nix_args=() if [ "${ABORT_ON_WARN-0}" = "1" ]; then local-nix-instantiate --option abort-on-warn true -E "$script" -A "$attr" else local-nix-instantiate -E "$script" -A "$attr" nix_args+=(--option abort-on-warn true) fi if [ "${STRICT_EVAL-0}" = "1" ]; then nix_args+=(--strict) fi local script="import ./default.nix { modules = [ $* ];}" local-nix-instantiate "${nix_args[@]}" -E "$script" -A "$attr" } reportFailure() { Loading Loading @@ -247,6 +254,19 @@ checkConfigError 'A definition for option .* is not of type .fileset.. Definitio checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err3 ./fileset.nix checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err4 ./fileset.nix # types.serializableValueWith checkConfigOutput '^null$' config.nullableValue.null ./types.nix checkConfigOutput '^true$' config.nullableValue.bool ./types.nix checkConfigOutput '^1$' config.nullableValue.int ./types.nix checkConfigOutput '^1.1$' config.nullableValue.float ./types.nix checkConfigOutput '^"foo"$' config.nullableValue.str ./types.nix checkConfigOutput '^".*/store.*"$' config.nullableValue.path ./types.nix STRICT_EVAL=1 checkConfigOutput '^\{"foo":1\}$' config.nullableValue.attrs ./types.nix STRICT_EVAL=1 checkConfigOutput '^\[\{"bar":\[1\]\}\]$' config.nullableValue.list ./types.nix checkConfigError 'A definition for option .* is not of type .VAL value.. .*' config.nullableValue.lambda ./types.nix checkConfigError 'A definition for option .* is not of type .VAL value.. .*' config.structuredValue.null ./types.nix # Check boolean option. checkConfigOutput '^false$' config.enable ./declare-enable.nix checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix Loading lib/tests/modules/types.nix +32 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,19 @@ in options = { pathInStore = mkOption { type = types.lazyAttrsOf types.pathInStore; }; externalPath = mkOption { type = types.lazyAttrsOf types.externalPath; }; # serializableValueWith nullableValue = mkOption { type = types.attrsOf (types.serializableValueWith { typeName = "VAL"; }); }; structuredValue = mkOption { type = types.attrsOf ( types.serializableValueWith { typeName = "VAL"; nullable = false; } ); }; assertions = mkOption { }; }; config = { Loading @@ -35,6 +48,22 @@ in externalPath.ok1 = "/foo/bar"; externalPath.ok2 = "/"; # serializableValueWith { nullable = true; } nullableValue.null = null; # null nullableValue.bool = true; # bool nullableValue.int = 1; # int nullableValue.float = 1.1; # float nullableValue.str = "foo"; # str nullableValue.path = ./.; # path nullableValue.attrs = { foo = 1; }; nullableValue.list = [ { bar = [ 1 ]; } ]; # list nullableValue.lambda = x: x; # Error # serializableValueWith { nullable = false; } structuredValue.null = null; # Error assertions = with lib.types; Loading Loading @@ -486,6 +515,9 @@ in assert (unique { message = "custom"; } (listOf str)).description == "list of string"; assert (unique { message = "test"; } (either int str)).description == "signed integer or string"; assert (unique { message = "test"; } (listOf str)).description == "list of string"; # json & toml assert json.description == "JSON value"; assert toml.description == "TOML value"; # done "ok"; }; Loading lib/types.nix +39 −0 Original line number Diff line number Diff line Loading @@ -1437,6 +1437,45 @@ rec { }; }; /** Creates a value type suitable for serialization formats. Parameters: - typeName: String describing the format (e.g. "JSON", "YAML", "XML") - nullable: Whether the structured value type allows `null` values. Returns a type suitable for structured data formats that supports: - Basic types: boolean, integer, float, string, path - Complex types: attribute sets and lists */ serializableValueWith = { typeName, nullable ? true, }: let baseType = oneOf [ bool int float str path (attrsOf valueType) (listOf valueType) ]; valueType = (if nullable then nullOr baseType else baseType) // { description = "${typeName} value"; }; in valueType; json = serializableValueWith { typeName = "JSON"; }; toml = serializableValueWith { typeName = "TOML"; nullable = false; }; # Either value of type `t1` or `t2`. either = t1: t2: Loading nixos/doc/manual/development/option-types.section.md +14 −0 Original line number Diff line number Diff line Loading @@ -497,6 +497,20 @@ Composed types are types that take a type as parameter. `listOf value of type *`to`*. Can be used to preserve backwards compatibility of an option if its type was changed. `types.json` : A type representing JSON-compatible values. This includes `null`, booleans, integers, floats, strings, paths, attribute sets, and lists. Attribute sets and lists can be arbitrarily nested and contain any JSON-compatible values. `types.toml` : A type representing TOML-compatible values. This includes booleans, integers, floats, strings, paths, attribute sets, and lists. Attribute sets and lists can be arbitrarily nested and contain any TOML-compatible values. ## Submodule {#section-option-types-submodule} `submodule` is a very powerful type that defines a set of sub-options Loading pkgs/pkgs-lib/formats.nix +8 −42 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ let ; inherit (lib.types) serializableValueWith attrsOf atom bool Loading @@ -58,38 +59,6 @@ let submodule ; /* Creates a structured value type suitable for serialization formats. Parameters: - typeName: String describing the format (e.g. "JSON", "YAML", "XML") - nullable: Whether the structured value type allows `null` values. Returns a type suitable for structured data formats that supports: - Basic types: boolean, integer, float, string, path - Complex types: attribute sets and lists */ mkStructuredType = { typeName, nullable ? true, }: let baseType = oneOf [ bool int float str path (attrsOf valueType) (listOf valueType) ]; valueType = (if nullable then nullOr baseType else baseType) // { description = "${typeName} value"; }; in valueType; # Attributes added accidentally in https://github.com/NixOS/nixpkgs/pull/335232 (2024-08-18) # Deprecated in https://github.com/NixOS/nixpkgs/pull/415666 (2025-06) allowAliases = pkgs.config.allowAliases or false; Loading Loading @@ -162,7 +131,7 @@ optionalAttrs allowAliases aliases { }: { type = mkStructuredType { typeName = "JSON"; }; type = types.json; generate = name: value: Loading Loading @@ -203,7 +172,7 @@ optionalAttrs allowAliases aliases '' ) { }; type = mkStructuredType { typeName = "YAML 1.1"; }; type = serializableValueWith { typeName = "YAML 1.1"; }; }; Loading @@ -226,7 +195,7 @@ optionalAttrs allowAliases aliases '' ) { }; type = mkStructuredType { typeName = "YAML 1.2"; }; type = serializableValueWith { typeName = "YAML 1.2"; }; }; Loading Loading @@ -487,10 +456,7 @@ optionalAttrs allowAliases aliases { }: json { } // { type = mkStructuredType { typeName = "TOML"; nullable = false; }; type = types.toml; generate = name: value: Loading Loading @@ -523,7 +489,7 @@ optionalAttrs allowAliases aliases { }: json { } // { type = mkStructuredType { typeName = "CDN"; }; type = serializableValueWith { typeName = "CDN"; }; generate = name: value: Loading Loading @@ -938,7 +904,7 @@ optionalAttrs allowAliases aliases pythonVars = { }: { type = attrsOf (mkStructuredType { type = attrsOf (serializableValueWith { typeName = "Python"; }); Loading Loading @@ -1020,7 +986,7 @@ optionalAttrs allowAliases aliases }: if format == "badgerfish" then { type = mkStructuredType { typeName = "XML"; }; type = serializableValueWith { typeName = "XML"; }; generate = name: value: Loading Loading
lib/tests/modules.sh +24 −4 Original line number Diff line number Diff line Loading @@ -58,12 +58,19 @@ logFailure() { evalConfig() { local attr=$1 shift local script="import ./default.nix { modules = [ $* ];}" local nix_args=() if [ "${ABORT_ON_WARN-0}" = "1" ]; then local-nix-instantiate --option abort-on-warn true -E "$script" -A "$attr" else local-nix-instantiate -E "$script" -A "$attr" nix_args+=(--option abort-on-warn true) fi if [ "${STRICT_EVAL-0}" = "1" ]; then nix_args+=(--strict) fi local script="import ./default.nix { modules = [ $* ];}" local-nix-instantiate "${nix_args[@]}" -E "$script" -A "$attr" } reportFailure() { Loading Loading @@ -247,6 +254,19 @@ checkConfigError 'A definition for option .* is not of type .fileset.. Definitio checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err3 ./fileset.nix checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err4 ./fileset.nix # types.serializableValueWith checkConfigOutput '^null$' config.nullableValue.null ./types.nix checkConfigOutput '^true$' config.nullableValue.bool ./types.nix checkConfigOutput '^1$' config.nullableValue.int ./types.nix checkConfigOutput '^1.1$' config.nullableValue.float ./types.nix checkConfigOutput '^"foo"$' config.nullableValue.str ./types.nix checkConfigOutput '^".*/store.*"$' config.nullableValue.path ./types.nix STRICT_EVAL=1 checkConfigOutput '^\{"foo":1\}$' config.nullableValue.attrs ./types.nix STRICT_EVAL=1 checkConfigOutput '^\[\{"bar":\[1\]\}\]$' config.nullableValue.list ./types.nix checkConfigError 'A definition for option .* is not of type .VAL value.. .*' config.nullableValue.lambda ./types.nix checkConfigError 'A definition for option .* is not of type .VAL value.. .*' config.structuredValue.null ./types.nix # Check boolean option. checkConfigOutput '^false$' config.enable ./declare-enable.nix checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix Loading
lib/tests/modules/types.nix +32 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,19 @@ in options = { pathInStore = mkOption { type = types.lazyAttrsOf types.pathInStore; }; externalPath = mkOption { type = types.lazyAttrsOf types.externalPath; }; # serializableValueWith nullableValue = mkOption { type = types.attrsOf (types.serializableValueWith { typeName = "VAL"; }); }; structuredValue = mkOption { type = types.attrsOf ( types.serializableValueWith { typeName = "VAL"; nullable = false; } ); }; assertions = mkOption { }; }; config = { Loading @@ -35,6 +48,22 @@ in externalPath.ok1 = "/foo/bar"; externalPath.ok2 = "/"; # serializableValueWith { nullable = true; } nullableValue.null = null; # null nullableValue.bool = true; # bool nullableValue.int = 1; # int nullableValue.float = 1.1; # float nullableValue.str = "foo"; # str nullableValue.path = ./.; # path nullableValue.attrs = { foo = 1; }; nullableValue.list = [ { bar = [ 1 ]; } ]; # list nullableValue.lambda = x: x; # Error # serializableValueWith { nullable = false; } structuredValue.null = null; # Error assertions = with lib.types; Loading Loading @@ -486,6 +515,9 @@ in assert (unique { message = "custom"; } (listOf str)).description == "list of string"; assert (unique { message = "test"; } (either int str)).description == "signed integer or string"; assert (unique { message = "test"; } (listOf str)).description == "list of string"; # json & toml assert json.description == "JSON value"; assert toml.description == "TOML value"; # done "ok"; }; Loading
lib/types.nix +39 −0 Original line number Diff line number Diff line Loading @@ -1437,6 +1437,45 @@ rec { }; }; /** Creates a value type suitable for serialization formats. Parameters: - typeName: String describing the format (e.g. "JSON", "YAML", "XML") - nullable: Whether the structured value type allows `null` values. Returns a type suitable for structured data formats that supports: - Basic types: boolean, integer, float, string, path - Complex types: attribute sets and lists */ serializableValueWith = { typeName, nullable ? true, }: let baseType = oneOf [ bool int float str path (attrsOf valueType) (listOf valueType) ]; valueType = (if nullable then nullOr baseType else baseType) // { description = "${typeName} value"; }; in valueType; json = serializableValueWith { typeName = "JSON"; }; toml = serializableValueWith { typeName = "TOML"; nullable = false; }; # Either value of type `t1` or `t2`. either = t1: t2: Loading
nixos/doc/manual/development/option-types.section.md +14 −0 Original line number Diff line number Diff line Loading @@ -497,6 +497,20 @@ Composed types are types that take a type as parameter. `listOf value of type *`to`*. Can be used to preserve backwards compatibility of an option if its type was changed. `types.json` : A type representing JSON-compatible values. This includes `null`, booleans, integers, floats, strings, paths, attribute sets, and lists. Attribute sets and lists can be arbitrarily nested and contain any JSON-compatible values. `types.toml` : A type representing TOML-compatible values. This includes booleans, integers, floats, strings, paths, attribute sets, and lists. Attribute sets and lists can be arbitrarily nested and contain any TOML-compatible values. ## Submodule {#section-option-types-submodule} `submodule` is a very powerful type that defines a set of sub-options Loading
pkgs/pkgs-lib/formats.nix +8 −42 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ let ; inherit (lib.types) serializableValueWith attrsOf atom bool Loading @@ -58,38 +59,6 @@ let submodule ; /* Creates a structured value type suitable for serialization formats. Parameters: - typeName: String describing the format (e.g. "JSON", "YAML", "XML") - nullable: Whether the structured value type allows `null` values. Returns a type suitable for structured data formats that supports: - Basic types: boolean, integer, float, string, path - Complex types: attribute sets and lists */ mkStructuredType = { typeName, nullable ? true, }: let baseType = oneOf [ bool int float str path (attrsOf valueType) (listOf valueType) ]; valueType = (if nullable then nullOr baseType else baseType) // { description = "${typeName} value"; }; in valueType; # Attributes added accidentally in https://github.com/NixOS/nixpkgs/pull/335232 (2024-08-18) # Deprecated in https://github.com/NixOS/nixpkgs/pull/415666 (2025-06) allowAliases = pkgs.config.allowAliases or false; Loading Loading @@ -162,7 +131,7 @@ optionalAttrs allowAliases aliases { }: { type = mkStructuredType { typeName = "JSON"; }; type = types.json; generate = name: value: Loading Loading @@ -203,7 +172,7 @@ optionalAttrs allowAliases aliases '' ) { }; type = mkStructuredType { typeName = "YAML 1.1"; }; type = serializableValueWith { typeName = "YAML 1.1"; }; }; Loading @@ -226,7 +195,7 @@ optionalAttrs allowAliases aliases '' ) { }; type = mkStructuredType { typeName = "YAML 1.2"; }; type = serializableValueWith { typeName = "YAML 1.2"; }; }; Loading Loading @@ -487,10 +456,7 @@ optionalAttrs allowAliases aliases { }: json { } // { type = mkStructuredType { typeName = "TOML"; nullable = false; }; type = types.toml; generate = name: value: Loading Loading @@ -523,7 +489,7 @@ optionalAttrs allowAliases aliases { }: json { } // { type = mkStructuredType { typeName = "CDN"; }; type = serializableValueWith { typeName = "CDN"; }; generate = name: value: Loading Loading @@ -938,7 +904,7 @@ optionalAttrs allowAliases aliases pythonVars = { }: { type = attrsOf (mkStructuredType { type = attrsOf (serializableValueWith { typeName = "Python"; }); Loading Loading @@ -1020,7 +986,7 @@ optionalAttrs allowAliases aliases }: if format == "badgerfish" then { type = mkStructuredType { typeName = "XML"; }; type = serializableValueWith { typeName = "XML"; }; generate = name: value: Loading