zaphyra's git: haumea

fork of https://github.com/nix-community/haumea

commit 6cc337b687cc73409f6fbfc49e313168547ff5e2
parent 94d3322007b5cab8ec5539fabc7848fe5270b1c0
Author: David Arnold <david.arnold@iohk.io>
Date: Mon, 10 Apr 2023 17:25:24 -0500

hoist attrs (#4)

transformers: add hoistAttrs

transformers: clean up hoistLists
9 files changed, 94 insertions(+), 14 deletions(-)
diff --git a/README.md b/README.md
@@ -191,6 +191,22 @@ This can be used to declare `imports` locally at the leaves
 of the configuration tree, where the module system would
 not otherwise tolerate them.
 
+### [`transformers.hoistAttrs`](src/transformers/hoistAttrs.nix)
+
+Type: `(from : String) -> (to : String) -> [ String ] -> { ... } -> { ... }`
+
+This transformer will hoist any attribute of type Attrs with key
+`${from}` up the chain. When the root node is reached, it will
+be renamed to an attribute of type Attrs with key `${to}` and
+as such presented back to the consumer.
+
+Neighbouring lists are concatenated (`recursiveUpdate`) during hoisting.
+Root doesn't concat `${from}` declarations, use `${to}` at the root.
+
+This can be used to declare `options` locally at the leaves
+of the configuration tree, where the module system would
+not otherwise tolerate them.
+
 ## Alternatives
 
 [std](https://github.com/divnix/std) is a more full-featured framework that also has filesystem-based auto-importing.
diff --git a/src/transformers/hoistAttrs.nix b/src/transformers/hoistAttrs.nix
@@ -0,0 +1,30 @@
+{ lib, super }:
+
+from: to:
+
+# Example from / to
+# - Lifting `options` from: _api, to: options
+#
+# Note:
+#   underscore used as mere convention to signalling to the user the  "private"
+#   nature, they won't be part of the final view presented to the user
+
+let
+  inherit (lib)
+    recursiveUpdate
+    ;
+  inherit (super.utils)
+    concatMapAttrsWith
+    ;
+in
+
+cursor:
+
+let toplevel = cursor == [ ]; in
+concatMapAttrsWith recursiveUpdate
+  (file: value: if ! value ? ${from} then { ${file} = value; } else {
+    ${file} = removeAttrs value [ from ];
+    # top level ${from} declarations are omitted from merging
+    ${if toplevel then to else from} = { ${file} = value.${from}; };
+  })
+
diff --git a/src/transformers/hoistLists.nix b/src/transformers/hoistLists.nix
@@ -31,17 +31,10 @@ in
 
 cursor:
 
-if cursor == [ ] # toplevel
-then
-  concatMapAttrsWith (mergeAttrsButConcatOn to)
-    (file: value: if ! value ? ${from} then { ${file} = value; } else {
-      ${file} = removeAttrs value [ from ];
-      # top level ${from} declarations are omitted from merging
-      ${to} = value.${from};
-    })
-else
-  concatMapAttrsWith (mergeAttrsButConcatOn from)
-    (file: value: if ! value ? ${from} then { ${file} = value; } else {
-      ${file} = removeAttrs value [ from ];
-      ${from} = value.${from};
-    })
+let toplevel = cursor == [ ]; in
+concatMapAttrsWith (mergeAttrsButConcatOn (if toplevel then to else from))
+  (file: value: if ! value ? ${from} then { ${file} = value; } else {
+    ${file} = removeAttrs value [ from ];
+    # top level ${from} declarations are omitted from merging
+    ${if toplevel then to else from} = value.${from};
+  })
diff --git a/tests/hoistAttrs/__fixture/bar.nix b/tests/hoistAttrs/__fixture/bar.nix
@@ -0,0 +1,5 @@
+{
+  _api = {
+    "tul" = "option";
+  };
+}
diff --git a/tests/hoistAttrs/__fixture/baz/default.nix b/tests/hoistAttrs/__fixture/baz/default.nix
@@ -0,0 +1,6 @@
+{
+  qux = "qux";
+  _api = {
+    "qui" = "option";
+  };
+}
diff --git a/tests/hoistAttrs/__fixture/default.nix b/tests/hoistAttrs/__fixture/default.nix
@@ -0,0 +1,3 @@
+{
+  options = { root = "option"; };
+}
diff --git a/tests/hoistAttrs/__fixture/foo.nix b/tests/hoistAttrs/__fixture/foo.nix
@@ -0,0 +1 @@
+"foo"
diff --git a/tests/hoistAttrs/expected.nix b/tests/hoistAttrs/expected.nix
@@ -0,0 +1,10 @@
+{
+  options = {
+    root = "option";
+    bar.tul = "option";
+    baz.qui = "option";
+  };
+  foo = "foo";
+  bar = { };
+  baz = { qux = "qux"; };
+}
diff --git a/tests/hoistAttrs/expr.nix b/tests/hoistAttrs/expr.nix
@@ -0,0 +1,16 @@
+{ haumea }:
+
+let
+  inherit (haumea.transformers)
+    liftDefault
+    hoistAttrs
+    ;
+in
+
+haumea.load {
+  src = ./__fixture;
+  transformer = [
+    liftDefault
+    (hoistAttrs "_api" "options")
+  ];
+}