mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-22 06:53:01 +00:00
formats.ini: disable merging as list by default
Previously, setting listsAsDuplicateKeys or listToValue would make it so merging these treat all values as lists, by coercing non-lists via lib.singleton. Some programs (such as gamemode; see #345121), allow some values to be repeated but not others, which can lead to unexpected behavior when non-list values are merged like this rather than throwing an error. This now makes that behavior opt-in via the mergeAsList option. Setting mergeAsList (to either true or false) without setting either listsAsDuplicateKeys or listToValue is an error, since lists are meaningless in this case.
This commit is contained in:
parent
e5e2a4b18e
commit
e14483d6a6
@ -566,6 +566,11 @@
|
|||||||
|
|
||||||
- The `rustic` package was upgrade to `0.9.0`, which contains [breaking changes to the config file format](https://github.com/rustic-rs/rustic/releases/tag/v0.9.0).
|
- The `rustic` package was upgrade to `0.9.0`, which contains [breaking changes to the config file format](https://github.com/rustic-rs/rustic/releases/tag/v0.9.0).
|
||||||
|
|
||||||
|
- `pkgs.formats.ini` and `pkgs.formats.iniWithGlobalSection` with
|
||||||
|
`listsAsDuplicateKeys` or `listToValue` no longer merge non-list values into
|
||||||
|
lists by default. Backwards-compatible behavior can be enabled with
|
||||||
|
`atomsCoercedToLists`.
|
||||||
|
|
||||||
## Other Notable Changes {#sec-release-24.11-notable-changes}
|
## Other Notable Changes {#sec-release-24.11-notable-changes}
|
||||||
|
|
||||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||||
|
@ -109,18 +109,21 @@ rec {
|
|||||||
singleIniAtom = nullOr (oneOf [ bool int float str ]) // {
|
singleIniAtom = nullOr (oneOf [ bool int float str ]) // {
|
||||||
description = "INI atom (null, bool, int, float or string)";
|
description = "INI atom (null, bool, int, float or string)";
|
||||||
};
|
};
|
||||||
iniAtom = { listsAsDuplicateKeys, listToValue }:
|
iniAtom = { listsAsDuplicateKeys, listToValue, atomsCoercedToLists }:
|
||||||
|
let
|
||||||
|
singleIniAtomOr = if atomsCoercedToLists then coercedTo singleIniAtom lib.singleton else either singleIniAtom;
|
||||||
|
in
|
||||||
if listsAsDuplicateKeys then
|
if listsAsDuplicateKeys then
|
||||||
coercedTo singleIniAtom lib.singleton (listOf singleIniAtom) // {
|
singleIniAtomOr (listOf singleIniAtom) // {
|
||||||
description = singleIniAtom.description + " or a list of them for duplicate keys";
|
description = singleIniAtom.description + " or a list of them for duplicate keys";
|
||||||
}
|
}
|
||||||
else if listToValue != null then
|
else if listToValue != null then
|
||||||
coercedTo singleIniAtom lib.singleton (nonEmptyListOf singleIniAtom) // {
|
singleIniAtomOr (nonEmptyListOf singleIniAtom) // {
|
||||||
description = singleIniAtom.description + " or a non-empty list of them";
|
description = singleIniAtom.description + " or a non-empty list of them";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
singleIniAtom;
|
singleIniAtom;
|
||||||
iniSection = { listsAsDuplicateKeys, listToValue }@args:
|
iniSection = { listsAsDuplicateKeys, listToValue, atomsCoercedToLists }@args:
|
||||||
attrsOf (iniAtom args) // {
|
attrsOf (iniAtom args) // {
|
||||||
description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
|
description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
|
||||||
};
|
};
|
||||||
@ -133,18 +136,26 @@ rec {
|
|||||||
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
||||||
# listToValue :: [IniAtom] -> IniAtom
|
# listToValue :: [IniAtom] -> IniAtom
|
||||||
listToValue ? null,
|
listToValue ? null,
|
||||||
|
# Merge multiple instances of the same key into a list
|
||||||
|
atomsCoercedToLists ? null,
|
||||||
...
|
...
|
||||||
}@args:
|
}@args:
|
||||||
assert listsAsDuplicateKeys -> listToValue == null;
|
assert listsAsDuplicateKeys -> listToValue == null;
|
||||||
|
assert atomsCoercedToLists != null -> (listsAsDuplicateKeys || listToValue != null);
|
||||||
|
let
|
||||||
|
atomsCoercedToLists' = if atomsCoercedToLists == null then false else atomsCoercedToLists;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
|
type = lib.types.attrsOf (
|
||||||
|
iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; }
|
||||||
|
);
|
||||||
|
|
||||||
generate = name: value:
|
generate = name: value:
|
||||||
lib.pipe value
|
lib.pipe value
|
||||||
[
|
[
|
||||||
(lib.mapAttrs (_: maybeToList listToValue))
|
(lib.mapAttrs (_: maybeToList listToValue))
|
||||||
(lib.generators.toINI (removeAttrs args ["listToValue"]))
|
(lib.generators.toINI (removeAttrs args ["listToValue" "atomsCoercedToLists"]))
|
||||||
(pkgs.writeText name)
|
(pkgs.writeText name)
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@ -155,26 +166,34 @@ rec {
|
|||||||
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
# Alternative to listsAsDuplicateKeys, converts list to non-list
|
||||||
# listToValue :: [IniAtom] -> IniAtom
|
# listToValue :: [IniAtom] -> IniAtom
|
||||||
listToValue ? null,
|
listToValue ? null,
|
||||||
|
# Merge multiple instances of the same key into a list
|
||||||
|
atomsCoercedToLists ? null,
|
||||||
...
|
...
|
||||||
}@args:
|
}@args:
|
||||||
assert listsAsDuplicateKeys -> listToValue == null;
|
assert listsAsDuplicateKeys -> listToValue == null;
|
||||||
|
assert atomsCoercedToLists != null -> (listsAsDuplicateKeys || listToValue != null);
|
||||||
|
let
|
||||||
|
atomsCoercedToLists' = if atomsCoercedToLists == null then false else atomsCoercedToLists;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
sections = lib.mkOption rec {
|
sections = lib.mkOption rec {
|
||||||
type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
|
type = lib.types.attrsOf (
|
||||||
|
iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; }
|
||||||
|
);
|
||||||
default = {};
|
default = {};
|
||||||
description = type.description;
|
description = type.description;
|
||||||
};
|
};
|
||||||
globalSection = lib.mkOption rec {
|
globalSection = lib.mkOption rec {
|
||||||
type = iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; };
|
type = iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; };
|
||||||
default = {};
|
default = {};
|
||||||
description = "global " + type.description;
|
description = "global " + type.description;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
generate = name: { sections ? {}, globalSection ? {}, ... }:
|
generate = name: { sections ? {}, globalSection ? {}, ... }:
|
||||||
pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue"])
|
pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue" "atomsCoercedToLists"])
|
||||||
{
|
{
|
||||||
globalSection = maybeToList listToValue globalSection;
|
globalSection = maybeToList listToValue globalSection;
|
||||||
sections = lib.mapAttrs (_: maybeToList listToValue) sections;
|
sections = lib.mapAttrs (_: maybeToList listToValue) sections;
|
||||||
@ -186,6 +205,7 @@ rec {
|
|||||||
atom = iniAtom {
|
atom = iniAtom {
|
||||||
listsAsDuplicateKeys = listsAsDuplicateKeys;
|
listsAsDuplicateKeys = listsAsDuplicateKeys;
|
||||||
listToValue = null;
|
listToValue = null;
|
||||||
|
atomsCoercedToLists = false;
|
||||||
};
|
};
|
||||||
in attrsOf (attrsOf (either atom (attrsOf atom)));
|
in attrsOf (attrsOf (either atom (attrsOf atom)));
|
||||||
|
|
||||||
|
@ -222,6 +222,67 @@ in runBuildTests {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
iniCoercedDuplicateKeys = shouldPass rec {
|
||||||
|
format = formats.ini {
|
||||||
|
listsAsDuplicateKeys = true;
|
||||||
|
atomsCoercedToLists = true;
|
||||||
|
};
|
||||||
|
input = format.type.merge [ ] [
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniCoercedDuplicateKeys";
|
||||||
|
value = { foo = { bar = 1; }; };
|
||||||
|
}
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniCoercedDuplicateKeys";
|
||||||
|
value = { foo = { bar = 2; }; };
|
||||||
|
}
|
||||||
|
];
|
||||||
|
expected = ''
|
||||||
|
[foo]
|
||||||
|
bar=1
|
||||||
|
bar=2
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
iniCoercedListToValue = shouldPass rec {
|
||||||
|
format = formats.ini {
|
||||||
|
listToValue = lib.concatMapStringsSep ", " (lib.generators.mkValueStringDefault { });
|
||||||
|
atomsCoercedToLists = true;
|
||||||
|
};
|
||||||
|
input = format.type.merge [ ] [
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniCoercedListToValue";
|
||||||
|
value = { foo = { bar = 1; }; };
|
||||||
|
}
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniCoercedListToValue";
|
||||||
|
value = { foo = { bar = 2; }; };
|
||||||
|
}
|
||||||
|
];
|
||||||
|
expected = ''
|
||||||
|
[foo]
|
||||||
|
bar=1, 2
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
iniCoercedNoLists = shouldFail {
|
||||||
|
format = formats.ini { atomsCoercedToLists = true; };
|
||||||
|
input = {
|
||||||
|
foo = {
|
||||||
|
bar = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
iniNoCoercedNoLists = shouldFail {
|
||||||
|
format = formats.ini { atomsCoercedToLists = false; };
|
||||||
|
input = {
|
||||||
|
foo = {
|
||||||
|
bar = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
iniWithGlobalNoSections = shouldPass {
|
iniWithGlobalNoSections = shouldPass {
|
||||||
format = formats.iniWithGlobalSection {};
|
format = formats.iniWithGlobalSection {};
|
||||||
input = {};
|
input = {};
|
||||||
@ -317,6 +378,82 @@ in runBuildTests {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
iniWithGlobalCoercedDuplicateKeys = shouldPass rec {
|
||||||
|
format = formats.iniWithGlobalSection {
|
||||||
|
listsAsDuplicateKeys = true;
|
||||||
|
atomsCoercedToLists = true;
|
||||||
|
};
|
||||||
|
input = format.type.merge [ ] [
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniWithGlobalCoercedDuplicateKeys";
|
||||||
|
value = {
|
||||||
|
globalSection = { baz = 4; };
|
||||||
|
sections = { foo = { bar = 1; }; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniWithGlobalCoercedDuplicateKeys";
|
||||||
|
value = {
|
||||||
|
globalSection = { baz = 3; };
|
||||||
|
sections = { foo = { bar = 2; }; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
expected = ''
|
||||||
|
baz=3
|
||||||
|
baz=4
|
||||||
|
|
||||||
|
[foo]
|
||||||
|
bar=2
|
||||||
|
bar=1
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
iniWithGlobalCoercedListToValue = shouldPass rec {
|
||||||
|
format = formats.iniWithGlobalSection {
|
||||||
|
listToValue = lib.concatMapStringsSep ", " (lib.generators.mkValueStringDefault { });
|
||||||
|
atomsCoercedToLists = true;
|
||||||
|
};
|
||||||
|
input = format.type.merge [ ] [
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniWithGlobalCoercedListToValue";
|
||||||
|
value = {
|
||||||
|
globalSection = { baz = 4; };
|
||||||
|
sections = { foo = { bar = 1; }; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
file = "format-test-inner-iniWithGlobalCoercedListToValue";
|
||||||
|
value = {
|
||||||
|
globalSection = { baz = 3; };
|
||||||
|
sections = { foo = { bar = 2; }; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
expected = ''
|
||||||
|
baz=3, 4
|
||||||
|
|
||||||
|
[foo]
|
||||||
|
bar=2, 1
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
iniWithGlobalCoercedNoLists = shouldFail {
|
||||||
|
format = formats.iniWithGlobalSection { atomsCoercedToLists = true; };
|
||||||
|
input = {
|
||||||
|
globalSection = { baz = 4; };
|
||||||
|
foo = { bar = 1; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
iniWithGlobalNoCoercedNoLists = shouldFail {
|
||||||
|
format = formats.iniWithGlobalSection { atomsCoercedToLists = false; };
|
||||||
|
input = {
|
||||||
|
globalSection = { baz = 4; };
|
||||||
|
foo = { bar = 1; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
keyValueAtoms = shouldPass {
|
keyValueAtoms = shouldPass {
|
||||||
format = formats.keyValue {};
|
format = formats.keyValue {};
|
||||||
input = {
|
input = {
|
||||||
|
Loading…
Reference in New Issue
Block a user