aboutsummaryrefslogtreecommitdiff
path: root/home-manager/modules/lib/types-dag.nix
blob: 4dbdb907b0e871de35b436651010e026c8150131 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{ dag, lib }:

with lib;

let

  isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before);

  dagContentType = elemType:
    types.submodule {
      options = {
        data = mkOption { type = elemType; };
        after = mkOption { type = with types; uniq (listOf str); };
        before = mkOption { type = with types; uniq (listOf str); };
      };
    };

in {
  # A directed acyclic graph of some inner type.
  dagOf = elemType:
    let
      convertAllToDags = let
        maybeConvert = n: v: if isDagEntry v then v else dag.entryAnywhere v;
      in map (def: def // { value = mapAttrs maybeConvert def.value; });

      attrEquivalent = types.attrsOf (dagContentType elemType);
    in mkOptionType rec {
      name = "dagOf";
      description = "DAG of ${elemType.description}s";
      check = isAttrs;
      merge = loc: defs: attrEquivalent.merge loc (convertAllToDags defs);
      getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]);
      getSubModules = elemType.getSubModules;
      substSubModules = m: dagOf (elemType.substSubModules m);
      functor = (defaultFunctor name) // { wrapped = elemType; };
    };

  # A directed acyclic graph of some inner type OR a list of that
  # inner type. This is a temporary hack for use by the
  # `programs.ssh.matchBlocks` and is only guaranteed to be vaguely
  # correct!
  #
  # In particular, adding a dependency on one of the "unnamed-N-M"
  # entries generated by a list value is almost guaranteed to destroy
  # the list's order.
  #
  # This function will be removed in version 20.09.
  listOrDagOf = elemType:
    let
      paddedIndexStr = list: i:
        let padWidth = stringLength (toString (length list));
        in fixedWidthNumber padWidth i;

      convertAllToDags = defs:
        let
          convertAttrValue = n: v:
            if isDagEntry v then v else dag.entryAnywhere v;

          convertListValue = namePrefix: vs:
            let
              pad = paddedIndexStr vs;
              makeEntry = i: v:
                nameValuePair "${namePrefix}.${pad i}" (dag.entryAnywhere v);
            in listToAttrs (imap1 makeEntry vs);

          convertValue = i: value:
            if isList value then
              convertListValue "unnamed-${paddedIndexStr defs i}" value
            else
              mapAttrs convertAttrValue value;
        in imap1 (i: def: def // { value = convertValue i def.value; }) defs;

      attrEquivalent = types.attrsOf (dagContentType elemType);
    in mkOptionType rec {
      name = "dagOf";
      description = "DAG of ${elemType.description}s";
      check = x: isAttrs x || isList x;
      merge = loc: defs: attrEquivalent.merge loc (convertAllToDags defs);
      getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]);
      getSubModules = elemType.getSubModules;
      substSubModules = m: dagOf (elemType.substSubModules m);
      functor = (defaultFunctor name) // { wrapped = elemType; };
    };
}