types: improve dagOf and listOrDagOf

In particular, improve the behavior of these types if the element type
is a submodule.
This commit is contained in:
Robert Helgesson 2020-04-13 22:01:20 +02:00
parent 9f223e98b7
commit 8ad4bd6c1b
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
3 changed files with 67 additions and 18 deletions

View file

@ -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 ++ [ "<name>" ]);
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; };
};
}

View file

@ -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
''
}
'';
};
}

View file

@ -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;