From 4a724cb84cc3aa464af1713d11bf0cfbbdb56c00 Mon Sep 17 00:00:00 2001
From: rcerc <88944439+rcerc@users.noreply.github.com>
Date: Thu, 13 Jan 2022 12:21:32 -0500
Subject: [PATCH] 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`.
---
 modules/lib/dag.nix       | 95 +++++++++++++++++----------------------
 modules/lib/default.nix   | 17 +------
 modules/lib/types-dag.nix | 12 +++--
 modules/lib/types.nix     |  5 +--
 4 files changed, 50 insertions(+), 79 deletions(-)

diff --git a/modules/lib/dag.nix b/modules/lib/dag.nix
index 0c0e33ee7..50044a0b7 100644
--- a/modules/lib/dag.nix
+++ b/modules/lib/dag.nix
@@ -4,39 +4,38 @@
 #
 #  - not specific to strings, i.e., any payload is OK,
 #
-#  - the addition of the function `dagEntryBefore` indicating a
-#    "wanted by" relationship.
+#  - the addition of the function `entryBefore` indicating a "wanted
+#    by" relationship.
 
 { lib }:
 
-let inherit (lib) all any filterAttrs mapAttrs mapAttrsToList toposort;
-in rec {
-
-  emptyDag = { };
+let inherit (lib) all filterAttrs hm mapAttrs toposort;
+in {
+  empty = { };
 
+  isEntry = e: e ? data && e ? after && e ? before;
   isDag = dag:
-    let isEntry = e: (e ? data) && (e ? after) && (e ? before);
-    in builtins.isAttrs dag && all (x: x) (mapAttrsToList (n: isEntry) dag);
+    builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
 
-  # Takes an attribute set containing entries built by
-  # dagEntryAnywhere, dagEntryAfter, and dagEntryBefore to a
-  # topologically sorted list of entries.
+  # Takes an attribute set containing entries built by entryAnywhere,
+  # entryAfter, and entryBefore to a topologically sorted list of
+  # entries.
   #
   # Internally this function uses the `toposort` function in
   # `<nixpkgs/lib/lists.nix>` and its value is accordingly.
   #
   # Specifically, the result on success is
   #
-  #    { result = [{name = ?; data = ?;} …] }
+  #    { result = [ { name = ?; data = ?; } … ] }
   #
   # For example
   #
-  #    nix-repl> dagTopoSort {
-  #                a = dagEntryAnywhere "1";
-  #                b = dagEntryAfter ["a" "c"] "2";
-  #                c = dagEntryBefore ["d"] "3";
-  #                d = dagEntryBefore ["e"] "4";
-  #                e = dagEntryAnywhere "5";
+  #    nix-repl> topoSort {
+  #                a = entryAnywhere "1";
+  #                b = entryAfter [ "a" "c" ] "2";
+  #                c = entryBefore [ "d" ] "3";
+  #                d = entryBefore [ "e" ] "4";
+  #                e = entryAnywhere "5";
   #              } == {
   #                result = [
   #                  { data = "1"; name = "a"; }
@@ -51,66 +50,54 @@ in rec {
   # And the result on error is
   #
   #    {
-  #      cycle = [ {after = ?; name = ?; data = ?} … ];
-  #      loops = [ {after = ?; name = ?; data = ?} … ];
+  #      cycle = [ { after = ?; name = ?; data = ? } … ];
+  #      loops = [ { after = ?; name = ?; data = ? } … ];
   #    }
   #
   # For example
   #
-  #    nix-repl> dagTopoSort {
-  #                a = dagEntryAnywhere "1";
-  #                b = dagEntryAfter ["a" "c"] "2";
-  #                c = dagEntryAfter ["d"] "3";
-  #                d = dagEntryAfter ["b"] "4";
-  #                e = dagEntryAnywhere "5";
+  #    nix-repl> topoSort {
+  #                a = entryAnywhere "1";
+  #                b = entryAfter [ "a" "c" ] "2";
+  #                c = entryAfter [ "d" ] "3";
+  #                d = entryAfter [ "b" ] "4";
+  #                e = entryAnywhere "5";
   #              } == {
   #                cycle = [
-  #                  { after = ["a" "c"]; data = "2"; name = "b"; }
-  #                  { after = ["d"]; data = "3"; name = "c"; }
-  #                  { after = ["b"]; data = "4"; name = "d"; }
+  #                  { after = [ "a" "c" ]; data = "2"; name = "b"; }
+  #                  { after = [ "d" ]; data = "3"; name = "c"; }
+  #                  { after = [ "b" ]; data = "4"; name = "d"; }
   #                ];
   #                loops = [
-  #                  { after = ["a" "c"]; data = "2"; name = "b"; }
+  #                  { after = [ "a" "c" ]; data = "2"; name = "b"; }
   #                ];
-  #              } == {}
+  #              }
   #    true
-  dagTopoSort = dag:
+  topoSort = dag:
     let
       dagBefore = dag: name:
-        mapAttrsToList (n: v: n)
-        (filterAttrs (n: v: any (a: a == name) v.before) dag);
+        builtins.attrNames
+        (filterAttrs (n: v: builtins.elem name v.before) dag);
       normalizedDag = mapAttrs (n: v: {
         name = n;
         data = v.data;
         after = v.after ++ dagBefore dag n;
       }) dag;
-      before = a: b: any (c: a.name == c) b.after;
-      sorted = toposort before (mapAttrsToList (n: v: v) normalizedDag);
+      before = a: b: builtins.elem a.name b.after;
+      sorted = toposort before (builtins.attrValues normalizedDag);
     in if sorted ? result then {
       result = map (v: { inherit (v) name data; }) sorted.result;
     } else
       sorted;
 
   # 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.
-  dagEntryAnywhere = data: {
-    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 = [ ];
-  };
+  entryAnywhere = hm.dag.entryBetween [ ] [ ];
 
+  entryAfter = hm.dag.entryBetween [ ];
+  entryBefore = before: hm.dag.entryBetween before [ ];
 }
diff --git a/modules/lib/default.nix b/modules/lib/default.nix
index 580316e09..59f707c4e 100644
--- a/modules/lib/default.nix
+++ b/modules/lib/default.nix
@@ -1,20 +1,7 @@
 { lib }:
 
 rec {
-  dag =
-    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;
-      };
+  dag = import ./dag.nix { inherit lib; };
 
   assertions = import ./assertions.nix { inherit lib; };
 
@@ -22,7 +9,7 @@ rec {
   gvariant = import ./gvariant.nix { inherit lib; };
   maintainers = import ./maintainers.nix;
   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; };
   zsh = import ./zsh.nix { inherit lib; };
diff --git a/modules/lib/types-dag.nix b/modules/lib/types-dag.nix
index e793940e3..46694f2ae 100644
--- a/modules/lib/types-dag.nix
+++ b/modules/lib/types-dag.nix
@@ -1,13 +1,11 @@
-{ dag, lib }:
+{ lib }:
 
 let
   inherit (lib)
-    concatStringsSep defaultFunctor fixedWidthNumber imap1 isAttrs isList length
-    listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair
+    concatStringsSep defaultFunctor fixedWidthNumber hm imap1 isAttrs isList
+    length listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair
     stringLength types warn;
 
-  isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before);
-
   dagEntryOf = elemType:
     let
       submoduleType = types.submodule ({ name, ... }: {
@@ -21,10 +19,10 @@ let
         };
       });
       maybeConvert = def:
-        if isDagEntry def.value then
+        if hm.dag.isEntry def.value then
           def.value
         else
-          dag.entryAnywhere (if def ? priority then
+          hm.dag.entryAnywhere (if def ? priority then
             mkOrder def.priority def.value
           else
             def.value);
diff --git a/modules/lib/types.nix b/modules/lib/types.nix
index 71ec587f1..d2ed24a3c 100644
--- a/modules/lib/types.nix
+++ b/modules/lib/types.nix
@@ -1,5 +1,4 @@
-{ lib, dag ? import ./dag.nix { inherit lib; }
-, gvariant ? import ./gvariant.nix { inherit lib; } }:
+{ lib, gvariant ? import ./gvariant.nix { inherit lib; } }:
 
 let
   inherit (lib)
@@ -7,7 +6,7 @@ let
     mergeAttrs mergeDefaultOption mergeOneOption mergeOptions mkOption
     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
   # must refer back to the type.