mirror of
https://github.com/nix-community/home-manager
synced 2024-11-26 21:19:45 +01:00
lib: add functions to create DAGs from lists
This commit is contained in:
parent
79e03fbe24
commit
28614ed7a1
4 changed files with 149 additions and 7 deletions
|
@ -11,12 +11,13 @@ The module system in Home Manager is based entirely on the NixOS module system s
|
|||
|
||||
Overall the basic option types are the same in Home Manager as NixOS. A few Home Manager options, however, make use of custom types that are worth describing in more detail. These are the option types `dagOf` and `gvariant` that are used, for example, by <<opt-programs.ssh.matchBlocks>> and <<opt-dconf.settings>>.
|
||||
|
||||
`hm.types.dagOf`::
|
||||
[[sec-option-types-dag]]`hm.types.dagOf`::
|
||||
Options of this type have attribute sets as values where each member is a node in a {wikipedia-dag}[directed acyclic graph] (DAG). This allows the attribute set entries to express dependency relations among themselves. This can, for example, be used to control the order of match blocks in a OpenSSH client configuration or the order of activation script blocks in <<opt-home.activation>>.
|
||||
+
|
||||
A number of functions are provided to create DAG nodes. The functions are shown below with examples using an option `foo.bar` of type `hm.types.dagOf types.int`.
|
||||
+
|
||||
`hm.dag.entryAnywhere (value: T)`:::
|
||||
--
|
||||
[[sec-option-types-dag-entryAnywhere]]`hm.dag.entryAnywhere (value: T) : DagEntry<T>`:::
|
||||
Indicates that `value` can be placed anywhere within the DAG. This is also the default for plain attribute set entries, that is
|
||||
+
|
||||
[source,nix]
|
||||
|
@ -37,7 +38,7 @@ foo.bar = {
|
|||
+
|
||||
are equivalent.
|
||||
+
|
||||
`hm.dag.entryAfter (afters: list string) (value: T)`:::
|
||||
[[sec-option-types-dag-entryAfter]]`hm.dag.entryAfter (afters: list string) (value: T) : DagEntry<T>` :::
|
||||
Indicates that `value` must be placed _after_ each of the attribute names in the given list. For example
|
||||
+
|
||||
[source,nix]
|
||||
|
@ -50,7 +51,7 @@ foo.bar = {
|
|||
+
|
||||
would place `b` after `a` in the graph.
|
||||
+
|
||||
`hm.dag.entryBefore (befores: list string) (value: T)`:::
|
||||
[[sec-option-types-dag-entryBefore]]`hm.dag.entryBefore (befores: list string) (value: T) : DagEntry<T>` :::
|
||||
Indicates that `value` must be placed _before_ each of the attribute names in the given list. For example
|
||||
+
|
||||
[source,nix]
|
||||
|
@ -63,7 +64,7 @@ foo.bar = {
|
|||
+
|
||||
would place `b` before `a` in the graph.
|
||||
+
|
||||
`hm.dag.entryBetween (befores: list string) (afters: list string) (value: T)`:::
|
||||
[[sec-option-types-dag-entryBetween]]`hm.dag.entryBetween (befores: list string) (afters: list string) (value: T) : DagEntry<T>` :::
|
||||
Indicates that `value` must be placed _before_ the attribute names in the first list and _after_ the attribute names in the second list. For example
|
||||
+
|
||||
[source,nix]
|
||||
|
@ -76,6 +77,93 @@ foo.bar = {
|
|||
----
|
||||
+
|
||||
would place `c` before `b` and after `a` in the graph.
|
||||
--
|
||||
+
|
||||
There are also a set of functions that generate a DAG from a list.
|
||||
These are convenient when you just want to have a linear list of DAG entries,
|
||||
without having to manually enter the relationship between each entry.
|
||||
Each of these functions take a `tag` as argument and the DAG entries will be named `${tag}-${index}`.
|
||||
|
||||
[[sec-option-types-dag-entriesAnywhere]]`hm.dag.entriesAnywhere (tag: string) (values: [T]) : Dag<T>`:::
|
||||
Creates a DAG with the given values with each entry labeled using the given tag. For example
|
||||
+
|
||||
[source,nix]
|
||||
foo.bar = hm.dag.entriesAnywhere "a" [ 0 1 ];
|
||||
+
|
||||
is equivalent to
|
||||
+
|
||||
[source,nix]
|
||||
----
|
||||
foo.bar = {
|
||||
a-0 = 0;
|
||||
a-1 = hm.dag.entryAfter [ "a-0" ] 1;
|
||||
}
|
||||
----
|
||||
+
|
||||
[[sec-option-types-dag-entriesAfter]]`hm.dag.entriesAfter (tag: string) (afters: list string) (values: [T]) : Dag<T>`:::
|
||||
Creates a DAG with the given values with each entry labeled using the given tag.
|
||||
The list of values are placed are placed _after_ each of the attribute names in `afters`.
|
||||
For example
|
||||
+
|
||||
[source,nix]
|
||||
foo.bar =
|
||||
{ b = 0; }
|
||||
// hm.dag.entriesAfter "a" [ "b" ] [ 1 2 ];
|
||||
+
|
||||
is equivalent to
|
||||
+
|
||||
[source,nix]
|
||||
----
|
||||
foo.bar = {
|
||||
b = 0;
|
||||
a-0 = hm.dag.entryAfter [ "b" ] 1;
|
||||
a-1 = hm.dag.entryAfter [ "a-0" ] 2;
|
||||
}
|
||||
----
|
||||
+
|
||||
[[sec-option-types-dag-entriesBefore]]`hm.dag.entriesBefore (tag: string) (befores: list string) (values: [T]) : Dag<T>`:::
|
||||
Creates a DAG with the given values with each entry labeled using the given tag.
|
||||
The list of values are placed _before_ each of the attribute names in `befores`.
|
||||
For example
|
||||
+
|
||||
[source,nix]
|
||||
foo.bar =
|
||||
{ b = 0; }
|
||||
// hm.dag.entriesBefore "a" [ "b" ] [ 1 2 ];
|
||||
+
|
||||
is equivalent to
|
||||
+
|
||||
[source,nix]
|
||||
----
|
||||
foo.bar = {
|
||||
b = 0;
|
||||
a-0 = 1;
|
||||
a-1 = hm.dag.entryBetween [ "b" ] [ "a-0" ] 2;
|
||||
}
|
||||
----
|
||||
+
|
||||
[[sec-option-types-dag-entriesBetween]]`hm.dag.entriesBetween (tag: string) (befores: list string) (afters: list string) (values: [T]) : Dag<T>`:::
|
||||
Creates a DAG with the given values with each entry labeled using the given tag.
|
||||
The list of values are placed _before_ each of the attribute names in `befores`
|
||||
and _after_ each of the attribute names in `afters`.
|
||||
For example
|
||||
+
|
||||
[source,nix]
|
||||
foo.bar =
|
||||
{ b = 0; c = 3; }
|
||||
// hm.dag.entriesBetween "a" [ "b" ] [ "c" ] [ 1 2 ];
|
||||
+
|
||||
is equivalent to
|
||||
+
|
||||
[source,nix]
|
||||
----
|
||||
foo.bar = {
|
||||
b = 0;
|
||||
c = 3;
|
||||
a-0 = hm.dag.entryAfter [ "c" ] 1;
|
||||
a-1 = hm.dag.entryBetween [ "b" ] [ "a-0" ] 2;
|
||||
}
|
||||
----
|
||||
|
||||
[[sec-option-types-gvariant]]`hm.types.gvariant`::
|
||||
This type is useful for options representing {gvariant-description}[GVariant] values. The type accepts all primitive GVariant types as well as arrays, tuples, ``maybe'' types, and dictionaries.
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
{ lib }:
|
||||
|
||||
let inherit (lib) all filterAttrs hm mapAttrs toposort;
|
||||
let inherit (lib) all filterAttrs head hm mapAttrs length tail toposort;
|
||||
in {
|
||||
empty = { };
|
||||
|
||||
|
@ -100,4 +100,30 @@ in {
|
|||
|
||||
entryAfter = hm.dag.entryBetween [ ];
|
||||
entryBefore = before: hm.dag.entryBetween before [ ];
|
||||
|
||||
# Given a list of entries, this function places them in order within the DAG.
|
||||
# Each entry is labeled "${tag}-${entry index}" and other DAG entries can be
|
||||
# added with 'before' or 'after' referring these indexed entries.
|
||||
#
|
||||
# The entries as a whole can be given a relation to other DAG nodes. All
|
||||
# generated nodes are then placed before or after those dependencies.
|
||||
entriesBetween = tag:
|
||||
let
|
||||
go = i: before: after: entries:
|
||||
let
|
||||
name = "${tag}-${toString i}";
|
||||
i' = i + 1;
|
||||
in if entries == [ ] then
|
||||
hm.dag.empty
|
||||
else if length entries == 1 then {
|
||||
"${name}" = hm.dag.entryBetween before after (head entries);
|
||||
} else
|
||||
{
|
||||
"${name}" = hm.dag.entryAfter after (head entries);
|
||||
} // go (i + 1) before [ name ] (tail entries);
|
||||
in go 0;
|
||||
|
||||
entriesAnywhere = tag: hm.dag.entriesBetween tag [ ] [ ];
|
||||
entriesAfter = tag: hm.dag.entriesBetween tag [ ];
|
||||
entriesBefore = tag: before: hm.dag.entriesBetween tag before [ ];
|
||||
}
|
||||
|
|
|
@ -2,3 +2,11 @@ before:before
|
|||
merged:left,middle,middle,right
|
||||
between:between
|
||||
after:after
|
||||
list-anywhere-0:list-anywhere-0
|
||||
list-before-0:list-before-0,sneaky-merge
|
||||
list-before-1:list-before-1
|
||||
list-anywhere-1:list-anywhere-1
|
||||
inside-list:inside-list
|
||||
list-after-0:list-after-0
|
||||
list-after-1:list-after-1
|
||||
list-anywhere-2:list-anywhere-2
|
||||
|
|
|
@ -27,7 +27,27 @@ in {
|
|||
{ merged = dag.entryBefore [ "between" ] "middle"; }
|
||||
{ merged = mkBefore "left"; }
|
||||
{ merged = dag.entryBetween [ "after" ] [ "before" ] (mkAfter "right"); }
|
||||
{ merged = dag.entryBefore [ "between" ] "middle"; }
|
||||
{
|
||||
merged = dag.entryBefore [ "between" ] "middle";
|
||||
}
|
||||
|
||||
# Some tests of list entries.
|
||||
(dag.entriesAnywhere "list-anywhere" [
|
||||
"list-anywhere-0"
|
||||
"list-anywhere-1"
|
||||
"list-anywhere-2"
|
||||
])
|
||||
{ inside-list = dag.entryAfter [ "list-anywhere-1" ] "inside-list"; }
|
||||
(dag.entriesBefore "list-before" [ "list-anywhere-1" ] [
|
||||
"list-before-0"
|
||||
"list-before-1"
|
||||
])
|
||||
(dag.entriesAfter "list-after" [ "list-before-0" ] [
|
||||
"list-after-0"
|
||||
"list-after-1"
|
||||
])
|
||||
(dag.entriesAnywhere "list-empty" [ ])
|
||||
{ "list-before-0" = mkAfter "sneaky-merge"; }
|
||||
];
|
||||
|
||||
home.file."result.txt".text = result;
|
||||
|
|
Loading…
Reference in a new issue