types: fix dagOf behaviour with mkIf

This makes definitions like

    home.activation.foo = mkIf false "bar"

work, where previously they would complain about
`home.activation.foobar.data` being used but not defined.

The crucial part is that we don't call `convertAllToDags` in
`dagOf.merge`, because we need to process `mkIf`/`mkMerge` properties
first. So we let `attrEquivalent.merge` do its job normally, but give
it a type `dagEntryOf` that does the conversion.

Ideally this shouldn't require so much boilerplate; I'd like to
implement something like

    types.changeInto dagContentType elemType dagEntryAnywhere

in Nixpkgs.

(cherry picked from commit 8db712a6a2)
This commit is contained in:
Naïm Favier 2022-03-23 05:01:52 +01:00
parent 236c6ec214
commit 834d863dd2
No known key found for this signature in database
GPG key ID: 49B07322580B7EE2
2 changed files with 33 additions and 24 deletions

View file

@ -8,17 +8,27 @@ let
isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before); isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before);
dagContentType = elemType: dagEntryOf = elemType:
types.submodule ({ name, ... }: { let
options = { submoduleType = types.submodule ({ name, ... }: {
data = mkOption { type = elemType; }; options = {
after = mkOption { type = with types; uniq (listOf str); }; data = mkOption { type = elemType; };
before = mkOption { type = with types; uniq (listOf str); }; 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; config = mkIf (elemType.name == "submodule") {
}; data._module.args.dagName = name;
}); };
});
maybeConvert = v: if isDagEntry v then v else dag.entryAnywhere v;
in mkOptionType {
name = "dagEntryOf";
description = "DAG entry of ${elemType.description}";
# leave the checking to the submodule type
merge = loc: defs:
submoduleType.merge loc
(map (def: def // { value = maybeConvert def.value; }) defs);
};
in rec { in rec {
# A directed acyclic graph of some inner type. # A directed acyclic graph of some inner type.
@ -29,21 +39,16 @@ in rec {
# "actual" attribute name a new submodule argument is provided with # "actual" attribute name a new submodule argument is provided with
# the name `dagName`. # the name `dagName`.
dagOf = elemType: dagOf = elemType:
let let attrEquivalent = types.attrsOf (dagEntryOf elemType);
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 { in mkOptionType rec {
name = "dagOf"; name = "dagOf";
description = "DAG of ${elemType.description}s"; description = "DAG of ${elemType.description}s";
check = isAttrs; inherit (attrEquivalent) check merge emptyValue;
merge = loc: defs: attrEquivalent.merge loc (convertAllToDags defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]);
getSubModules = elemType.getSubModules; getSubModules = elemType.getSubModules;
substSubModules = m: dagOf (elemType.substSubModules m); substSubModules = m: dagOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; }; functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
}; };
# A directed acyclic graph of some inner type OR a list of that # A directed acyclic graph of some inner type OR a list of that

View file

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
inherit (lib) concatStringsSep hm mkMerge mkOption types; inherit (lib) concatStringsSep hm mkIf mkMerge mkOption types;
dag = lib.hm.dag; dag = lib.hm.dag;
@ -14,10 +14,14 @@ in {
options.tested.dag = mkOption { type = hm.types.dagOf types.str; }; options.tested.dag = mkOption { type = hm.types.dagOf types.str; };
config = { config = {
tested = mkMerge [ tested.dag = mkMerge [
{ dag.after = "after"; } { never = mkIf false "never"; }
{ dag.before = dag.entryBefore [ "after" ] "before"; } { after = mkMerge [ "after" (mkIf false "neither") ]; }
{ dag.between = dag.entryBetween [ "after" ] [ "before" ] "between"; } { before = dag.entryBefore [ "after" ] (mkIf true "before"); }
{
between =
mkIf true (dag.entryBetween [ "after" ] [ "before" ] "between");
}
]; ];
home.file."result.txt".text = result; home.file."result.txt".text = result;