Unverified Commit 77ecd8ea authored by Kira Bruneau's avatar Kira Bruneau Committed by GitHub
Browse files

formats.ini: disable merging as list by default (#346299)

parents 0b22769d e14483d6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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).

- `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}

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+29 −9
Original line number Diff line number Diff line
@@ -109,18 +109,21 @@ rec {
      singleIniAtom = nullOr (oneOf [ bool int float str ]) // {
        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
          coercedTo singleIniAtom lib.singleton (listOf singleIniAtom) // {
          singleIniAtomOr (listOf singleIniAtom) // {
            description = singleIniAtom.description + " or a list of them for duplicate keys";
          }
        else if listToValue != null then
          coercedTo singleIniAtom lib.singleton (nonEmptyListOf singleIniAtom) // {
          singleIniAtomOr (nonEmptyListOf singleIniAtom) // {
            description = singleIniAtom.description + " or a non-empty list of them";
          }
        else
          singleIniAtom;
      iniSection = { listsAsDuplicateKeys, listToValue }@args:
      iniSection = { listsAsDuplicateKeys, listToValue, atomsCoercedToLists }@args:
        attrsOf (iniAtom args) // {
          description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
        };
@@ -133,18 +136,26 @@ rec {
        # Alternative to listsAsDuplicateKeys, converts list to non-list
        # listToValue :: [IniAtom] -> IniAtom
        listToValue ? null,
        # Merge multiple instances of the same key into a list
        atomsCoercedToLists ? null,
        ...
        }@args:
        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:
          lib.pipe value
          [
            (lib.mapAttrs (_: maybeToList listToValue))
            (lib.generators.toINI (removeAttrs args ["listToValue"]))
            (lib.generators.toINI (removeAttrs args ["listToValue" "atomsCoercedToLists"]))
            (pkgs.writeText name)
          ];
      };
@@ -155,26 +166,34 @@ rec {
        # Alternative to listsAsDuplicateKeys, converts list to non-list
        # listToValue :: [IniAtom] -> IniAtom
        listToValue ? null,
        # Merge multiple instances of the same key into a list
        atomsCoercedToLists ? null,
        ...
        }@args:
        assert listsAsDuplicateKeys -> listToValue == null;
        assert atomsCoercedToLists != null -> (listsAsDuplicateKeys || listToValue != null);
        let
          atomsCoercedToLists' = if atomsCoercedToLists == null then false else atomsCoercedToLists;
        in
        {
          type = lib.types.submodule {
            options = {
              sections = lib.mkOption rec {
                type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
                type = lib.types.attrsOf (
                  iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; }
                );
                default = {};
                description = type.description;
              };
              globalSection = lib.mkOption rec {
                type = iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; };
                type = iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; };
                default = {};
                description = "global " + type.description;
              };
            };
          };
          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;
              sections = lib.mapAttrs (_: maybeToList listToValue) sections;
@@ -186,6 +205,7 @@ rec {
          atom = iniAtom {
            listsAsDuplicateKeys = listsAsDuplicateKeys;
            listToValue = null;
            atomsCoercedToLists = false;
          };
        in attrsOf (attrsOf (either atom (attrsOf atom)));

+137 −0
Original line number Diff line number Diff line
@@ -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 {
    format = formats.iniWithGlobalSection {};
    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 {
    format = formats.keyValue {};
    input = {