pkgs.formats: toINIWithGlobalSection wrapper
The new format is based on the existing wrapper and generates an INI file with an unnamed global section at the top as is used by *stunnel* for instance. Technically the INI format is a subset of this however testing, type checking, and API guarantees profit from two separate generators. Co-authored-by: tim-tx <tim-tx@users.noreply.github.com> Signed-off-by: benaryorg <binary@benary.org>
This commit is contained in:
parent
3e72e7c014
commit
8b2d86b982
3 changed files with 199 additions and 45 deletions
|
@ -73,6 +73,34 @@ have a predefined type and string generator already declared under
|
|||
|
||||
It returns a set with INI-specific attributes `type` and `generate`
|
||||
as specified [below](#pkgs-formats-result).
|
||||
The type of the input is an *attrset* of sections; key-value pairs where
|
||||
the key is the section name and the value is the corresponding content
|
||||
which is also an *attrset* of key-value pairs for the actual key-value
|
||||
mappings of the INI format.
|
||||
The values of the INI atoms are subject to the above parameters (e.g. lists
|
||||
may be transformed into multiple key-value pairs depending on
|
||||
`listToValue`).
|
||||
|
||||
`pkgs.formats.iniWithGlobalSection` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \.\.\. }
|
||||
|
||||
: A function taking an attribute set with values
|
||||
|
||||
`listsAsDuplicateKeys`
|
||||
|
||||
: A boolean for controlling whether list values can be used to
|
||||
represent duplicate INI keys
|
||||
|
||||
`listToValue`
|
||||
|
||||
: A function for turning a list of values into a single value.
|
||||
|
||||
It returns a set with INI-specific attributes `type` and `generate`
|
||||
as specified [below](#pkgs-formats-result).
|
||||
The type of the input is an *attrset* of the structure
|
||||
`{ sections = {}; globalSection = {}; }` where *sections* are several
|
||||
sections as with *pkgs.formats.ini* and *globalSection* being just a single
|
||||
attrset of key-value pairs for a single section, the global section which
|
||||
preceedes the section definitions.
|
||||
|
||||
`pkgs.formats.toml` { }
|
||||
|
||||
|
|
|
@ -95,29 +95,13 @@ rec {
|
|||
|
||||
};
|
||||
|
||||
ini = {
|
||||
# Represents lists as duplicate keys
|
||||
listsAsDuplicateKeys ? false,
|
||||
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
||||
# listToValue :: [IniAtom] -> IniAtom
|
||||
listToValue ? null,
|
||||
...
|
||||
}@args:
|
||||
assert !listsAsDuplicateKeys || listToValue == null;
|
||||
{
|
||||
|
||||
type = with lib.types; let
|
||||
|
||||
singleIniAtom = nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
float
|
||||
str
|
||||
]) // {
|
||||
# the ini formats share a lot of code
|
||||
inherit (
|
||||
let
|
||||
singleIniAtom = with lib.types; nullOr (oneOf [ bool int float str ]) // {
|
||||
description = "INI atom (null, bool, int, float or string)";
|
||||
};
|
||||
|
||||
iniAtom =
|
||||
iniAtom = with lib.types; { listsAsDuplicateKeys, listToValue }:
|
||||
if listsAsDuplicateKeys then
|
||||
coercedTo singleIniAtom lib.singleton (listOf singleIniAtom) // {
|
||||
description = singleIniAtom.description + " or a list of them for duplicate keys";
|
||||
|
@ -128,21 +112,79 @@ rec {
|
|||
}
|
||||
else
|
||||
singleIniAtom;
|
||||
iniSection = with lib.types; { listsAsDuplicateKeys, listToValue }@args:
|
||||
attrsOf (iniAtom args) // {
|
||||
description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
|
||||
};
|
||||
|
||||
in attrsOf (attrsOf iniAtom);
|
||||
maybeToList = listToValue: if listToValue != null then lib.mapAttrs (key: val: if lib.isList val then listToValue val else val) else lib.id;
|
||||
in {
|
||||
ini = {
|
||||
# Represents lists as duplicate keys
|
||||
listsAsDuplicateKeys ? false,
|
||||
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
||||
# listToValue :: [IniAtom] -> IniAtom
|
||||
listToValue ? null,
|
||||
...
|
||||
}@args:
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
{
|
||||
|
||||
generate = name: value:
|
||||
let
|
||||
transformedValue =
|
||||
if listToValue != null
|
||||
then
|
||||
lib.mapAttrs (section: lib.mapAttrs (key: val:
|
||||
if lib.isList val then listToValue val else val
|
||||
)) value
|
||||
else value;
|
||||
in pkgs.writeText name (lib.generators.toINI (removeAttrs args ["listToValue"]) transformedValue);
|
||||
type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
|
||||
|
||||
};
|
||||
generate = name: value:
|
||||
lib.pipe value
|
||||
[
|
||||
(lib.mapAttrs (_: maybeToList listToValue))
|
||||
(lib.generators.toINI (removeAttrs args ["listToValue"]))
|
||||
(pkgs.writeText name)
|
||||
];
|
||||
};
|
||||
|
||||
iniWithGlobalSection = {
|
||||
# Represents lists as duplicate keys
|
||||
listsAsDuplicateKeys ? false,
|
||||
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
||||
# listToValue :: [IniAtom] -> IniAtom
|
||||
listToValue ? null,
|
||||
...
|
||||
}@args:
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
{
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
sections = lib.mkOption rec {
|
||||
type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
|
||||
default = {};
|
||||
description = type.description;
|
||||
};
|
||||
globalSection = lib.mkOption rec {
|
||||
type = iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; };
|
||||
default = {};
|
||||
description = "global " + type.description;
|
||||
};
|
||||
};
|
||||
};
|
||||
generate = name: { sections ? {}, globalSection ? {}, ... }:
|
||||
pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue"])
|
||||
{
|
||||
globalSection = maybeToList listToValue globalSection;
|
||||
sections = lib.mapAttrs (_: maybeToList listToValue) sections;
|
||||
});
|
||||
};
|
||||
|
||||
gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
|
||||
type = let
|
||||
atom = iniAtom {
|
||||
listsAsDuplicateKeys = listsAsDuplicateKeys;
|
||||
listToValue = null;
|
||||
};
|
||||
in with lib.types; attrsOf (attrsOf (either atom (attrsOf atom)));
|
||||
|
||||
generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
|
||||
};
|
||||
|
||||
}) ini iniWithGlobalSection gitIni;
|
||||
|
||||
# As defined by systemd.syntax(7)
|
||||
#
|
||||
|
@ -166,7 +208,7 @@ rec {
|
|||
listToValue ? null,
|
||||
...
|
||||
}@args:
|
||||
assert !listsAsDuplicateKeys || listToValue == null;
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
{
|
||||
|
||||
type = with lib.types; let
|
||||
|
@ -207,17 +249,6 @@ rec {
|
|||
|
||||
};
|
||||
|
||||
gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
|
||||
|
||||
type = with lib.types; let
|
||||
|
||||
iniAtom = (ini args).type/*attrsOf*/.functor.wrapped/*attrsOf*/.functor.wrapped;
|
||||
|
||||
in attrsOf (attrsOf (either iniAtom (attrsOf iniAtom)));
|
||||
|
||||
generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
|
||||
};
|
||||
|
||||
toml = {}: json {} // {
|
||||
type = with lib.types; let
|
||||
valueType = oneOf [
|
||||
|
|
|
@ -222,6 +222,101 @@ in runBuildTests {
|
|||
'';
|
||||
};
|
||||
|
||||
iniWithGlobalNoSections = shouldPass {
|
||||
format = formats.iniWithGlobalSection {};
|
||||
input = {};
|
||||
expected = "";
|
||||
};
|
||||
|
||||
iniWithGlobalOnlySections = shouldPass {
|
||||
format = formats.iniWithGlobalSection {};
|
||||
input = {
|
||||
sections = {
|
||||
foo = {
|
||||
bar = "baz";
|
||||
};
|
||||
};
|
||||
};
|
||||
expected = ''
|
||||
[foo]
|
||||
bar=baz
|
||||
'';
|
||||
};
|
||||
|
||||
iniWithGlobalOnlyGlobal = shouldPass {
|
||||
format = formats.iniWithGlobalSection {};
|
||||
input = {
|
||||
globalSection = {
|
||||
bar = "baz";
|
||||
};
|
||||
};
|
||||
expected = ''
|
||||
bar=baz
|
||||
|
||||
'';
|
||||
};
|
||||
|
||||
iniWithGlobalWrongSections = shouldFail {
|
||||
format = formats.iniWithGlobalSection {};
|
||||
input = {
|
||||
foo = {};
|
||||
};
|
||||
};
|
||||
|
||||
iniWithGlobalEverything = shouldPass {
|
||||
format = formats.iniWithGlobalSection {};
|
||||
input = {
|
||||
globalSection = {
|
||||
bar = true;
|
||||
};
|
||||
sections = {
|
||||
foo = {
|
||||
bool = true;
|
||||
int = 10;
|
||||
float = 3.141;
|
||||
str = "string";
|
||||
};
|
||||
};
|
||||
};
|
||||
expected = ''
|
||||
bar=true
|
||||
|
||||
[foo]
|
||||
bool=true
|
||||
float=3.141000
|
||||
int=10
|
||||
str=string
|
||||
'';
|
||||
};
|
||||
|
||||
iniWithGlobalListToValue = shouldPass {
|
||||
format = formats.iniWithGlobalSection { listToValue = lib.concatMapStringsSep ", " (lib.generators.mkValueStringDefault {}); };
|
||||
input = {
|
||||
globalSection = {
|
||||
bar = [ null true "test" 1.2 10 ];
|
||||
baz = false;
|
||||
qux = "qux";
|
||||
};
|
||||
sections = {
|
||||
foo = {
|
||||
bar = [ null true "test" 1.2 10 ];
|
||||
baz = false;
|
||||
qux = "qux";
|
||||
};
|
||||
};
|
||||
};
|
||||
expected = ''
|
||||
bar=null, true, test, 1.200000, 10
|
||||
baz=false
|
||||
qux=qux
|
||||
|
||||
[foo]
|
||||
bar=null, true, test, 1.200000, 10
|
||||
baz=false
|
||||
qux=qux
|
||||
'';
|
||||
};
|
||||
|
||||
keyValueAtoms = shouldPass {
|
||||
format = formats.keyValue {};
|
||||
input = {
|
||||
|
|
Loading…
Reference in a new issue