commit 6cc337b687cc73409f6fbfc49e313168547ff5e2
parent 94d3322007b5cab8ec5539fabc7848fe5270b1c0
Author: David Arnold <david.arnold@iohk.io>
Date: Mon, 10 Apr 2023 17:25:24 -0500
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") + ]; +}