diff --git a/nixos/doc/manual/development/settings-options.section.md b/nixos/doc/manual/development/settings-options.section.md index f3257a56d71b..8675c1ef08a5 100644 --- a/nixos/doc/manual/development/settings-options.section.md +++ b/nixos/doc/manual/development/settings-options.section.md @@ -322,6 +322,14 @@ have a predefined type and string generator already declared under The attribute `lib.type.atom` contains the used INI atom. +`pkgs.formats.configobj` { } + +: A function taking an attribute set with values + + It returns a set with [ConfigObj](https://pypi.org/project/configobj/)-specific attributes `type` and `generate` as specified [below](#pkgs-formats-result). + The type of the input is an attribute mapping supporting both atoms and nested attribute sets (sections/subsections), as supported by ConfigObj. + The renderer is based on Python's `configobj` module. + `pkgs.formats.iniWithGlobalSection` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \.\.\. } : A function taking an attribute set with values diff --git a/pkgs/pkgs-lib/formats.nix b/pkgs/pkgs-lib/formats.nix index e6806816d1c8..cd42759bdf98 100644 --- a/pkgs/pkgs-lib/formats.nix +++ b/pkgs/pkgs-lib/formats.nix @@ -131,6 +131,8 @@ optionalAttrs allowAliases aliases php = (import ./formats/php/default.nix { inherit lib pkgs; }).format; + configobj = (import ./formats/configobj/default.nix { inherit lib pkgs; }).format; + json = { }: { diff --git a/pkgs/pkgs-lib/formats/configobj/default.nix b/pkgs/pkgs-lib/formats/configobj/default.nix new file mode 100644 index 000000000000..8179c4412e5f --- /dev/null +++ b/pkgs/pkgs-lib/formats/configobj/default.nix @@ -0,0 +1,36 @@ +{ + lib, + pkgs, +}: +let + inherit (lib) + toJSON + ; + + inherit (lib.types) + serializableValueWith + ; +in +{ + format = + { }: + { + type = serializableValueWith { typeName = "ConfigObj mapping"; }; + + generate = + name: value: + pkgs.runCommandLocal name + { + nativeBuildInputs = [ + (pkgs.python3.withPackages (ps: [ ps.configobj ])) + ]; + + valuesJSON = toJSON value; + __structuredAttrs = true; + strictDeps = true; + } + '' + printf "%s" "$valuesJSON" | python3 ${./generate.py} > "$out" + ''; + }; +} diff --git a/pkgs/pkgs-lib/formats/configobj/generate.py b/pkgs/pkgs-lib/formats/configobj/generate.py new file mode 100644 index 000000000000..5863396c120d --- /dev/null +++ b/pkgs/pkgs-lib/formats/configobj/generate.py @@ -0,0 +1,7 @@ +import json +import sys +from configobj import ConfigObj + +config = ConfigObj(interpolation=False, encoding="UTF8") +config.update(json.load(sys.stdin)) +config.write(sys.stdout.buffer) diff --git a/pkgs/pkgs-lib/tests/formats.nix b/pkgs/pkgs-lib/tests/formats.nix index 7e3f6457588f..ee6d817287c2 100644 --- a/pkgs/pkgs-lib/tests/formats.nix +++ b/pkgs/pkgs-lib/tests/formats.nix @@ -229,6 +229,110 @@ runBuildTests { ''; }; + configobjAtoms = shouldPass { + format = formats.configobj { }; + input = { + bool = true; + int = 10; + float = 3.141; + str = "string"; + }; + expected = '' + bool = True + float = 3.141 + int = 10 + str = string + ''; + }; + + configobjListWithoutListToValue = shouldPass { + format = formats.configobj { }; + input = { + items = [ + 1 + true + "x" + ]; + }; + expected = '' + items = 1, True, x + ''; + }; + + configobjNestedAttrsets = shouldPass { + format = formats.configobj { }; + input = { + server = { + host = "127.0.0.1"; + port = 8080; + enabled = true; + tags = [ + "web" + "nix" + 42 + ]; + }; + + logging = { + level = "info"; + rotate = true; + }; + + interfaces = { + local = { + address = "123"; + coin = { + foo = "bar"; + }; + }; + remote = { + address = "456"; + }; + }; + }; + expected = '' + [interfaces] + [[local]] + address = 123 + [[[coin]]] + foo = bar + [[remote]] + address = 456 + [logging] + level = info + rotate = True + [server] + enabled = True + host = 127.0.0.1 + port = 8080 + tags = web, nix, 42 + ''; + }; + + configobjNullableValues = shouldPass { + format = formats.configobj { }; + input = { + nullable = null; + nested = { + keep = "ok"; + missing = null; + }; + }; + expected = '' + nullable = None + [nested] + keep = ok + missing = None + ''; + }; + + configobjInvalidAtom = shouldFail { + format = formats.configobj { }; + input = { + function = _: 1; + }; + }; + iniInvalidAtom = shouldFail { format = formats.ini { }; input = {