diff --git a/lib/generators.nix b/lib/generators.nix index a64e94bd5cb..240a19789b5 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -76,10 +76,14 @@ rec { * mkKeyValue is the same as in toINI. */ toKeyValue = { - mkKeyValue ? mkKeyValueDefault {} "=" - }: attrs: - let mkLine = k: v: mkKeyValue k v + "\n"; - in libStr.concatStrings (libAttr.mapAttrsToList mkLine attrs); + mkKeyValue ? mkKeyValueDefault {} "=", + listsAsDuplicateKeys ? false + }: + let mkLine = k: v: mkKeyValue k v + "\n"; + mkLines = if listsAsDuplicateKeys + then k: v: map (mkLine k) (if lib.isList v then v else [v]) + else k: v: [ (mkLine k v) ]; + in attrs: libStr.concatStrings (lib.concatLists (libAttr.mapAttrsToList mkLines attrs)); /* Generate an INI-style config file from an @@ -106,7 +110,9 @@ rec { # apply transformations (e.g. escapes) to section names mkSectionName ? (name: libStr.escape [ "[" "]" ] name), # format a setting line from key and value - mkKeyValue ? mkKeyValueDefault {} "=" + mkKeyValue ? mkKeyValueDefault {} "=", + # allow lists as values for duplicate keys + listsAsDuplicateKeys ? false }: attrsOfAttrs: let # map function to string for each key val @@ -115,7 +121,7 @@ rec { (libAttr.mapAttrsToList mapFn attrs); mkSection = sectName: sectValues: '' [${mkSectionName sectName}] - '' + toKeyValue { inherit mkKeyValue; } sectValues; + '' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues; in # map input to ini sections mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 01ff5ecf148..739c5d5fe15 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -348,6 +348,18 @@ runTests { ''; }; + testToINIDuplicateKeys = { + expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; }; + expected = '' + [baz] + qux=1 + qux=false + + [foo] + bar=true + ''; + }; + testToINIDefaultEscapes = { expr = generators.toINI {} { "no [ and ] allowed unescaped" = {