diff --git a/modules/lib/types-dag.nix b/modules/lib/types-dag.nix index 4dbdb907b..a50ea2d12 100644 --- a/modules/lib/types-dag.nix +++ b/modules/lib/types-dag.nix @@ -7,16 +7,25 @@ let isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before); dagContentType = elemType: - types.submodule { + types.submodule ({ name, ... }: { options = { data = mkOption { type = elemType; }; after = mkOption { type = with types; uniq (listOf str); }; before = mkOption { type = with types; uniq (listOf str); }; }; - }; + config = mkIf (elemType.name == "submodule") { + data._module.args.dagName = name; + }; + }); -in { +in rec { # A directed acyclic graph of some inner type. + # + # Note, if the element type is a submodule then the `name` argument + # will always be set to the string "data" since it picks up the + # internal structure of the DAG values. To give access to the + # "actual" attribute name a new submodule argument is provided with + # the name `dagName`. dagOf = elemType: let convertAllToDags = let @@ -51,34 +60,30 @@ in { let padWidth = stringLength (toString (length list)); in fixedWidthNumber padWidth i; - convertAllToDags = defs: + convertAll = 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); + makeEntry = i: v: nameValuePair "${namePrefix}.${pad i}" v; in listToAttrs (imap1 makeEntry vs); convertValue = i: value: if isList value then convertListValue "unnamed-${paddedIndexStr defs i}" value else - mapAttrs convertAttrValue value; + value; in imap1 (i: def: def // { value = convertValue i def.value; }) defs; - attrEquivalent = types.attrsOf (dagContentType elemType); + dagType = dagOf 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 ++ [ "" ]); - getSubModules = elemType.getSubModules; - substSubModules = m: dagOf (elemType.substSubModules m); + name = "listOrDagOf"; + description = "list or DAG of ${elemType.description}s"; + check = x: isList x || dagType.check x; + merge = loc: defs: dagType.merge loc (convertAll defs); + getSubOptions = dagType.getSubOptions; + getSubModules = dagType.getSubModules; + substSubModules = m: listOrDagOf (elemType.substSubModules m); functor = (defaultFunctor name) // { wrapped = elemType; }; }; } diff --git a/tests/lib/types/dag-submodule.nix b/tests/lib/types/dag-submodule.nix new file mode 100644 index 000000000..552e804ac --- /dev/null +++ b/tests/lib/types/dag-submodule.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dag = config.lib.dag; + + result = let + sorted = dag.topoSort config.tested.dag; + data = map (e: "${e.name}:${e.data.name}") sorted.result; + in concatStringsSep "\n" data + "\n"; + +in { + options.tested.dag = mkOption { + type = hm.types.dagOf (types.submodule ({ dagName, ... }: { + options.name = mkOption { type = types.str; }; + config.name = "dn-${dagName}"; + })); + }; + + config = { + tested.dag = { + after = { }; + before = dag.entryBefore [ "after" ] { }; + between = dag.entryBetween [ "after" ] [ "before" ] { }; + }; + + home.file."result.txt".text = result; + + nmt.script = '' + assertFileContent \ + home-files/result.txt \ + ${ + pkgs.writeText "result.txt" '' + before:dn-before + between:dn-between + after:dn-after + '' + } + ''; + }; +} diff --git a/tests/lib/types/default.nix b/tests/lib/types/default.nix index bc5cfbaba..acb565012 100644 --- a/tests/lib/types/default.nix +++ b/tests/lib/types/default.nix @@ -1,4 +1,5 @@ { + lib-types-dag-submodule = ./dag-submodule.nix; lib-types-dag-merge = ./dag-merge.nix; lib-types-list-or-dag-merge = ./list-or-dag-merge.nix;