mirror of
https://github.com/nix-community/home-manager
synced 2024-11-23 11:39:46 +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>>.
|
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>>.
|
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`.
|
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
|
Indicates that `value` can be placed anywhere within the DAG. This is also the default for plain attribute set entries, that is
|
||||||
+
|
+
|
||||||
[source,nix]
|
[source,nix]
|
||||||
|
@ -37,7 +38,7 @@ foo.bar = {
|
||||||
+
|
+
|
||||||
are equivalent.
|
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
|
Indicates that `value` must be placed _after_ each of the attribute names in the given list. For example
|
||||||
+
|
+
|
||||||
[source,nix]
|
[source,nix]
|
||||||
|
@ -50,7 +51,7 @@ foo.bar = {
|
||||||
+
|
+
|
||||||
would place `b` after `a` in the graph.
|
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
|
Indicates that `value` must be placed _before_ each of the attribute names in the given list. For example
|
||||||
+
|
+
|
||||||
[source,nix]
|
[source,nix]
|
||||||
|
@ -63,7 +64,7 @@ foo.bar = {
|
||||||
+
|
+
|
||||||
would place `b` before `a` in the graph.
|
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
|
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]
|
[source,nix]
|
||||||
|
@ -76,6 +77,93 @@ foo.bar = {
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
would place `c` before `b` and after `a` in the graph.
|
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`::
|
[[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.
|
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 }:
|
{ lib }:
|
||||||
|
|
||||||
let inherit (lib) all filterAttrs hm mapAttrs toposort;
|
let inherit (lib) all filterAttrs head hm mapAttrs length tail toposort;
|
||||||
in {
|
in {
|
||||||
empty = { };
|
empty = { };
|
||||||
|
|
||||||
|
@ -100,4 +100,30 @@ in {
|
||||||
|
|
||||||
entryAfter = hm.dag.entryBetween [ ];
|
entryAfter = hm.dag.entryBetween [ ];
|
||||||
entryBefore = before: hm.dag.entryBetween before [ ];
|
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
|
merged:left,middle,middle,right
|
||||||
between:between
|
between:between
|
||||||
after:after
|
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 = dag.entryBefore [ "between" ] "middle"; }
|
||||||
{ merged = mkBefore "left"; }
|
{ merged = mkBefore "left"; }
|
||||||
{ merged = dag.entryBetween [ "after" ] [ "before" ] (mkAfter "right"); }
|
{ 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;
|
home.file."result.txt".text = result;
|
||||||
|
|
Loading…
Reference in a new issue