lib: improve DAG library

Specifically,

- directly export `modules/lib/dag.nix` instead of renaming
  attributes,

- run through utilities to reuse code where possible,

- expose `lib.hm.dag.isEntry` and reuse it in
  `modules/lib/types-dag.nix`,

- reuse utilities through `lib` set instead of passing imports to
  functions, and

- eta reduction of `map`, `entryAnywhere`, `entryAfter` and
  `entryBefore`.
This commit is contained in:
rcerc 2022-01-13 12:21:32 -05:00 committed by Robert Helgesson
parent 30dda628bc
commit 4a724cb84c
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
4 changed files with 50 additions and 79 deletions

View file

@ -4,39 +4,38 @@
# #
# - not specific to strings, i.e., any payload is OK, # - not specific to strings, i.e., any payload is OK,
# #
# - the addition of the function `dagEntryBefore` indicating a # - the addition of the function `entryBefore` indicating a "wanted
# "wanted by" relationship. # by" relationship.
{ lib }: { lib }:
let inherit (lib) all any filterAttrs mapAttrs mapAttrsToList toposort; let inherit (lib) all filterAttrs hm mapAttrs toposort;
in rec { in {
empty = { };
emptyDag = { };
isEntry = e: e ? data && e ? after && e ? before;
isDag = dag: isDag = dag:
let isEntry = e: (e ? data) && (e ? after) && (e ? before); builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
in builtins.isAttrs dag && all (x: x) (mapAttrsToList (n: isEntry) dag);
# Takes an attribute set containing entries built by # Takes an attribute set containing entries built by entryAnywhere,
# dagEntryAnywhere, dagEntryAfter, and dagEntryBefore to a # entryAfter, and entryBefore to a topologically sorted list of
# topologically sorted list of entries. # entries.
# #
# Internally this function uses the `toposort` function in # Internally this function uses the `toposort` function in
# `<nixpkgs/lib/lists.nix>` and its value is accordingly. # `<nixpkgs/lib/lists.nix>` and its value is accordingly.
# #
# Specifically, the result on success is # Specifically, the result on success is
# #
# { result = [{name = ?; data = ?;} …] } # { result = [ { name = ?; data = ?; } … ] }
# #
# For example # For example
# #
# nix-repl> dagTopoSort { # nix-repl> topoSort {
# a = dagEntryAnywhere "1"; # a = entryAnywhere "1";
# b = dagEntryAfter ["a" "c"] "2"; # b = entryAfter [ "a" "c" ] "2";
# c = dagEntryBefore ["d"] "3"; # c = entryBefore [ "d" ] "3";
# d = dagEntryBefore ["e"] "4"; # d = entryBefore [ "e" ] "4";
# e = dagEntryAnywhere "5"; # e = entryAnywhere "5";
# } == { # } == {
# result = [ # result = [
# { data = "1"; name = "a"; } # { data = "1"; name = "a"; }
@ -51,66 +50,54 @@ in rec {
# And the result on error is # And the result on error is
# #
# { # {
# cycle = [ {after = ?; name = ?; data = ?} … ]; # cycle = [ { after = ?; name = ?; data = ? } … ];
# loops = [ {after = ?; name = ?; data = ?} … ]; # loops = [ { after = ?; name = ?; data = ? } … ];
# } # }
# #
# For example # For example
# #
# nix-repl> dagTopoSort { # nix-repl> topoSort {
# a = dagEntryAnywhere "1"; # a = entryAnywhere "1";
# b = dagEntryAfter ["a" "c"] "2"; # b = entryAfter [ "a" "c" ] "2";
# c = dagEntryAfter ["d"] "3"; # c = entryAfter [ "d" ] "3";
# d = dagEntryAfter ["b"] "4"; # d = entryAfter [ "b" ] "4";
# e = dagEntryAnywhere "5"; # e = entryAnywhere "5";
# } == { # } == {
# cycle = [ # cycle = [
# { after = ["a" "c"]; data = "2"; name = "b"; } # { after = [ "a" "c" ]; data = "2"; name = "b"; }
# { after = ["d"]; data = "3"; name = "c"; } # { after = [ "d" ]; data = "3"; name = "c"; }
# { after = ["b"]; data = "4"; name = "d"; } # { after = [ "b" ]; data = "4"; name = "d"; }
# ]; # ];
# loops = [ # loops = [
# { after = ["a" "c"]; data = "2"; name = "b"; } # { after = [ "a" "c" ]; data = "2"; name = "b"; }
# ]; # ];
# } == {} # }
# true # true
dagTopoSort = dag: topoSort = dag:
let let
dagBefore = dag: name: dagBefore = dag: name:
mapAttrsToList (n: v: n) builtins.attrNames
(filterAttrs (n: v: any (a: a == name) v.before) dag); (filterAttrs (n: v: builtins.elem name v.before) dag);
normalizedDag = mapAttrs (n: v: { normalizedDag = mapAttrs (n: v: {
name = n; name = n;
data = v.data; data = v.data;
after = v.after ++ dagBefore dag n; after = v.after ++ dagBefore dag n;
}) dag; }) dag;
before = a: b: any (c: a.name == c) b.after; before = a: b: builtins.elem a.name b.after;
sorted = toposort before (mapAttrsToList (n: v: v) normalizedDag); sorted = toposort before (builtins.attrValues normalizedDag);
in if sorted ? result then { in if sorted ? result then {
result = map (v: { inherit (v) name data; }) sorted.result; result = map (v: { inherit (v) name data; }) sorted.result;
} else } else
sorted; sorted;
# Applies a function to each element of the given DAG. # Applies a function to each element of the given DAG.
dagMap = f: dag: mapAttrs (n: v: v // { data = f n v.data; }) dag; map = f: mapAttrs (n: v: v // { data = f n v.data; });
entryBetween = before: after: data: { inherit data before after; };
# Create a DAG entry with no particular dependency information. # Create a DAG entry with no particular dependency information.
dagEntryAnywhere = data: { entryAnywhere = hm.dag.entryBetween [ ] [ ];
inherit data;
before = [ ];
after = [ ];
};
dagEntryBetween = before: after: data: { inherit data before after; };
dagEntryAfter = after: data: {
inherit data after;
before = [ ];
};
dagEntryBefore = before: data: {
inherit data before;
after = [ ];
};
entryAfter = hm.dag.entryBetween [ ];
entryBefore = before: hm.dag.entryBetween before [ ];
} }

View file

@ -1,20 +1,7 @@
{ lib }: { lib }:
rec { rec {
dag = dag = import ./dag.nix { inherit lib; };
let
d = import ./dag.nix { inherit lib; };
in
{
empty = d.emptyDag;
isDag = d.isDag;
topoSort = d.dagTopoSort;
map = d.dagMap;
entryAnywhere = d.dagEntryAnywhere;
entryBetween = d.dagEntryBetween;
entryAfter = d.dagEntryAfter;
entryBefore = d.dagEntryBefore;
};
assertions = import ./assertions.nix { inherit lib; }; assertions = import ./assertions.nix { inherit lib; };
@ -22,7 +9,7 @@ rec {
gvariant = import ./gvariant.nix { inherit lib; }; gvariant = import ./gvariant.nix { inherit lib; };
maintainers = import ./maintainers.nix; maintainers = import ./maintainers.nix;
strings = import ./strings.nix { inherit lib; }; strings = import ./strings.nix { inherit lib; };
types = import ./types.nix { inherit dag gvariant lib; }; types = import ./types.nix { inherit gvariant lib; };
shell = import ./shell.nix { inherit lib; }; shell = import ./shell.nix { inherit lib; };
zsh = import ./zsh.nix { inherit lib; }; zsh = import ./zsh.nix { inherit lib; };

View file

@ -1,13 +1,11 @@
{ dag, lib }: { lib }:
let let
inherit (lib) inherit (lib)
concatStringsSep defaultFunctor fixedWidthNumber imap1 isAttrs isList length concatStringsSep defaultFunctor fixedWidthNumber hm imap1 isAttrs isList
listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair length listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair
stringLength types warn; stringLength types warn;
isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before);
dagEntryOf = elemType: dagEntryOf = elemType:
let let
submoduleType = types.submodule ({ name, ... }: { submoduleType = types.submodule ({ name, ... }: {
@ -21,10 +19,10 @@ let
}; };
}); });
maybeConvert = def: maybeConvert = def:
if isDagEntry def.value then if hm.dag.isEntry def.value then
def.value def.value
else else
dag.entryAnywhere (if def ? priority then hm.dag.entryAnywhere (if def ? priority then
mkOrder def.priority def.value mkOrder def.priority def.value
else else
def.value); def.value);

View file

@ -1,5 +1,4 @@
{ lib, dag ? import ./dag.nix { inherit lib; } { lib, gvariant ? import ./gvariant.nix { inherit lib; } }:
, gvariant ? import ./gvariant.nix { inherit lib; } }:
let let
inherit (lib) inherit (lib)
@ -7,7 +6,7 @@ let
mergeAttrs mergeDefaultOption mergeOneOption mergeOptions mkOption mergeAttrs mergeDefaultOption mergeOneOption mergeOptions mkOption
mkOptionType showFiles showOption types; mkOptionType showFiles showOption types;
typesDag = import ./types-dag.nix { inherit dag lib; }; typesDag = import ./types-dag.nix { inherit lib; };
# Needed since the type is called gvariant and its merge attribute # Needed since the type is called gvariant and its merge attribute
# must refer back to the type. # must refer back to the type.