1{ lib }:
2# Operations on attribute sets.
3
4let
5 inherit (builtins) head tail length;
6 inherit (lib.trivial) id;
7 inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName;
8 inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl;
9in
10
11rec {
12 inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
13
14
15 /* Return an attribute from nested attribute sets.
16
17 Example:
18 x = { a = { b = 3; }; }
19 attrByPath ["a" "b"] 6 x
20 => 3
21 attrByPath ["z" "z"] 6 x
22 => 6
23 */
24 attrByPath = attrPath: default: e:
25 let attr = head attrPath;
26 in
27 if attrPath == [] then e
28 else if e ? ${attr}
29 then attrByPath (tail attrPath) default e.${attr}
30 else default;
31
32 /* Return if an attribute from nested attribute set exists.
33
34 Example:
35 x = { a = { b = 3; }; }
36 hasAttrByPath ["a" "b"] x
37 => true
38 hasAttrByPath ["z" "z"] x
39 => false
40
41 */
42 hasAttrByPath = attrPath: e:
43 let attr = head attrPath;
44 in
45 if attrPath == [] then true
46 else if e ? ${attr}
47 then hasAttrByPath (tail attrPath) e.${attr}
48 else false;
49
50
51 /* Return nested attribute set in which an attribute is set.
52
53 Example:
54 setAttrByPath ["a" "b"] 3
55 => { a = { b = 3; }; }
56 */
57 setAttrByPath = attrPath: value:
58 let
59 len = length attrPath;
60 atDepth = n:
61 if n == len
62 then value
63 else { ${elemAt attrPath n} = atDepth (n + 1); };
64 in atDepth 0;
65
66 /* Like `attrByPath' without a default value. If it doesn't find the
67 path it will throw.
68
69 Example:
70 x = { a = { b = 3; }; }
71 getAttrFromPath ["a" "b"] x
72 => 3
73 getAttrFromPath ["z" "z"] x
74 => error: cannot find attribute `z.z'
75 */
76 getAttrFromPath = attrPath:
77 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
78 in attrByPath attrPath (abort errorMsg);
79
80
81 /* Update or set specific paths of an attribute set.
82
83 Takes a list of updates to apply and an attribute set to apply them to,
84 and returns the attribute set with the updates applied. Updates are
85 represented as { path = ...; update = ...; } values, where `path` is a
86 list of strings representing the attribute path that should be updated,
87 and `update` is a function that takes the old value at that attribute path
88 as an argument and returns the new
89 value it should be.
90
91 Properties:
92 - Updates to deeper attribute paths are applied before updates to more
93 shallow attribute paths
94 - Multiple updates to the same attribute path are applied in the order
95 they appear in the update list
96 - If any but the last `path` element leads into a value that is not an
97 attribute set, an error is thrown
98 - If there is an update for an attribute path that doesn't exist,
99 accessing the argument in the update function causes an error, but
100 intermediate attribute sets are implicitly created as needed
101
102 Example:
103 updateManyAttrsByPath [
104 {
105 path = [ "a" "b" ];
106 update = old: { d = old.c; };
107 }
108 {
109 path = [ "a" "b" "c" ];
110 update = old: old + 1;
111 }
112 {
113 path = [ "x" "y" ];
114 update = old: "xy";
115 }
116 ] { a.b.c = 0; }
117 => { a = { b = { d = 1; }; }; x = { y = "xy"; }; }
118 */
119 updateManyAttrsByPath = let
120 # When recursing into attributes, instead of updating the `path` of each
121 # update using `tail`, which needs to allocate an entirely new list,
122 # we just pass a prefix length to use and make sure to only look at the
123 # path without the prefix length, so that we can reuse the original list
124 # entries.
125 go = prefixLength: hasValue: value: updates:
126 let
127 # Splits updates into ones on this level (split.right)
128 # And ones on levels further down (split.wrong)
129 split = partition (el: length el.path == prefixLength) updates;
130
131 # Groups updates on further down levels into the attributes they modify
132 nested = groupBy (el: elemAt el.path prefixLength) split.wrong;
133
134 # Applies only nested modification to the input value
135 withNestedMods =
136 # Return the value directly if we don't have any nested modifications
137 if split.wrong == [] then
138 if hasValue then value
139 else
140 # Throw an error if there is no value. This `head` call here is
141 # safe, but only in this branch since `go` could only be called
142 # with `hasValue == false` for nested updates, in which case
143 # it's also always called with at least one update
144 let updatePath = (head split.right).path; in
145 throw
146 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does "
147 + "not exist in the given value, but the first update to this "
148 + "path tries to access the existing value.")
149 else
150 # If there are nested modifications, try to apply them to the value
151 if ! hasValue then
152 # But if we don't have a value, just use an empty attribute set
153 # as the value, but simplify the code a bit
154 mapAttrs (name: go (prefixLength + 1) false null) nested
155 else if isAttrs value then
156 # If we do have a value and it's an attribute set, override it
157 # with the nested modifications
158 value //
159 mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested
160 else
161 # However if it's not an attribute set, we can't apply the nested
162 # modifications, throw an error
163 let updatePath = (head split.wrong).path; in
164 throw
165 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to "
166 + "be updated, but path '${showAttrPath (take prefixLength updatePath)}' "
167 + "of the given value is not an attribute set, so we can't "
168 + "update an attribute inside of it.");
169
170 # We get the final result by applying all the updates on this level
171 # after having applied all the nested updates
172 # We use foldl instead of foldl' so that in case of multiple updates,
173 # intermediate values aren't evaluated if not needed
174 in foldl (acc: el: el.update acc) withNestedMods split.right;
175
176 in updates: value: go 0 true value updates;
177
178 /* Return the specified attributes from a set.
179
180 Example:
181 attrVals ["a" "b" "c"] as
182 => [as.a as.b as.c]
183 */
184 attrVals = nameList: set: map (x: set.${x}) nameList;
185
186
187 /* Return the values of all attributes in the given set, sorted by
188 attribute name.
189
190 Example:
191 attrValues {c = 3; a = 1; b = 2;}
192 => [1 2 3]
193 */
194 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs);
195
196
197 /* Given a set of attribute names, return the set of the corresponding
198 attributes from the given set.
199
200 Example:
201 getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; }
202 => { a = 1; b = 2; }
203 */
204 getAttrs = names: attrs: genAttrs names (name: attrs.${name});
205
206 /* Collect each attribute named `attr' from a list of attribute
207 sets. Sets that don't contain the named attribute are ignored.
208
209 Example:
210 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
211 => [1 2]
212 */
213 catAttrs = builtins.catAttrs or
214 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l));
215
216
217 /* Filter an attribute set by removing all attributes for which the
218 given predicate return false.
219
220 Example:
221 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
222 => { foo = 1; }
223 */
224 filterAttrs = pred: set:
225 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
226
227
228 /* Filter an attribute set recursively by removing all attributes for
229 which the given predicate return false.
230
231 Example:
232 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; }
233 => { foo = {}; }
234 */
235 filterAttrsRecursive = pred: set:
236 listToAttrs (
237 concatMap (name:
238 let v = set.${name}; in
239 if pred name v then [
240 (nameValuePair name (
241 if isAttrs v then filterAttrsRecursive pred v
242 else v
243 ))
244 ] else []
245 ) (attrNames set)
246 );
247
248 /* Apply fold functions to values grouped by key.
249
250 Example:
251 foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }]
252 => { a = [ 2 3 ]; }
253 */
254 foldAttrs = op: nul:
255 foldr (n: a:
256 foldr (name: o:
257 o // { ${name} = op n.${name} (a.${name} or nul); }
258 ) a (attrNames n)
259 ) {};
260
261
262 /* Recursively collect sets that verify a given predicate named `pred'
263 from the set `attrs'. The recursion is stopped when the predicate is
264 verified.
265
266 Type:
267 collect ::
268 (AttrSet -> Bool) -> AttrSet -> [x]
269
270 Example:
271 collect isList { a = { b = ["b"]; }; c = [1]; }
272 => [["b"] [1]]
273
274 collect (x: x ? outPath)
275 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
276 => [{ outPath = "a/"; } { outPath = "b/"; }]
277 */
278 collect = pred: attrs:
279 if pred attrs then
280 [ attrs ]
281 else if isAttrs attrs then
282 concatMap (collect pred) (attrValues attrs)
283 else
284 [];
285
286 /* Return the cartesian product of attribute set value combinations.
287
288 Example:
289 cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
290 => [
291 { a = 1; b = 10; }
292 { a = 1; b = 20; }
293 { a = 2; b = 10; }
294 { a = 2; b = 20; }
295 ]
296 */
297 cartesianProductOfSets = attrsOfLists:
298 foldl' (listOfAttrs: attrName:
299 concatMap (attrs:
300 map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
301 ) listOfAttrs
302 ) [{}] (attrNames attrsOfLists);
303
304
305 /* Utility function that creates a {name, value} pair as expected by
306 builtins.listToAttrs.
307
308 Example:
309 nameValuePair "some" 6
310 => { name = "some"; value = 6; }
311 */
312 nameValuePair = name: value: { inherit name value; };
313
314
315 /* Apply a function to each element in an attribute set. The
316 function takes two arguments --- the attribute name and its value
317 --- and returns the new value for the attribute. The result is a
318 new attribute set.
319
320 Example:
321 mapAttrs (name: value: name + "-" + value)
322 { x = "foo"; y = "bar"; }
323 => { x = "x-foo"; y = "y-bar"; }
324 */
325 mapAttrs = builtins.mapAttrs or
326 (f: set:
327 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));
328
329
330 /* Like `mapAttrs', but allows the name of each attribute to be
331 changed in addition to the value. The applied function should
332 return both the new name and value as a `nameValuePair'.
333
334 Example:
335 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
336 { x = "a"; y = "b"; }
337 => { foo_x = "bar-a"; foo_y = "bar-b"; }
338 */
339 mapAttrs' = f: set:
340 listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
341
342
343 /* Call a function for each attribute in the given set and return
344 the result in a list.
345
346 Type:
347 mapAttrsToList ::
348 (String -> a -> b) -> AttrSet -> [b]
349
350 Example:
351 mapAttrsToList (name: value: name + value)
352 { x = "a"; y = "b"; }
353 => [ "xa" "yb" ]
354 */
355 mapAttrsToList = f: attrs:
356 map (name: f name attrs.${name}) (attrNames attrs);
357
358
359 /* Like `mapAttrs', except that it recursively applies itself to
360 attribute sets. Also, the first argument of the argument
361 function is a *list* of the names of the containing attributes.
362
363 Type:
364 mapAttrsRecursive ::
365 ([String] -> a -> b) -> AttrSet -> AttrSet
366
367 Example:
368 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
369 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
370 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
371 */
372 mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
373
374
375 /* Like `mapAttrsRecursive', but it takes an additional predicate
376 function that tells it whether to recurse into an attribute
377 set. If it returns false, `mapAttrsRecursiveCond' does not
378 recurse, but does apply the map function. If it returns true, it
379 does recurse, and does not apply the map function.
380
381 Type:
382 mapAttrsRecursiveCond ::
383 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
384
385 Example:
386 # To prevent recursing into derivations (which are attribute
387 # sets with the attribute "type" equal to "derivation"):
388 mapAttrsRecursiveCond
389 (as: !(as ? "type" && as.type == "derivation"))
390 (x: ... do something ...)
391 attrs
392 */
393 mapAttrsRecursiveCond = cond: f: set:
394 let
395 recurse = path:
396 let
397 g =
398 name: value:
399 if isAttrs value && cond value
400 then recurse (path ++ [name]) value
401 else f (path ++ [name]) value;
402 in mapAttrs g;
403 in recurse [] set;
404
405
406 /* Generate an attribute set by mapping a function over a list of
407 attribute names.
408
409 Example:
410 genAttrs [ "foo" "bar" ] (name: "x_" + name)
411 => { foo = "x_foo"; bar = "x_bar"; }
412 */
413 genAttrs = names: f:
414 listToAttrs (map (n: nameValuePair n (f n)) names);
415
416
417 /* Check whether the argument is a derivation. Any set with
418 { type = "derivation"; } counts as a derivation.
419
420 Example:
421 nixpkgs = import <nixpkgs> {}
422 isDerivation nixpkgs.ruby
423 => true
424 isDerivation "foobar"
425 => false
426 */
427 isDerivation = x: x.type or null == "derivation";
428
429 /* Converts a store path to a fake derivation. */
430 toDerivation = path:
431 let
432 path' = builtins.storePath path;
433 res =
434 { type = "derivation";
435 name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
436 outPath = path';
437 outputs = [ "out" ];
438 out = res;
439 outputName = "out";
440 };
441 in res;
442
443
444 /* If `cond' is true, return the attribute set `as',
445 otherwise an empty attribute set.
446
447 Example:
448 optionalAttrs (true) { my = "set"; }
449 => { my = "set"; }
450 optionalAttrs (false) { my = "set"; }
451 => { }
452 */
453 optionalAttrs = cond: as: if cond then as else {};
454
455
456 /* Merge sets of attributes and use the function f to merge attributes
457 values.
458
459 Example:
460 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
461 => { a = ["x" "y"]; }
462 */
463 zipAttrsWithNames = names: f: sets:
464 listToAttrs (map (name: {
465 inherit name;
466 value = f name (catAttrs name sets);
467 }) names);
468
469 /* Implementation note: Common names appear multiple times in the list of
470 names, hopefully this does not affect the system because the maximal
471 laziness avoid computing twice the same expression and listToAttrs does
472 not care about duplicated attribute names.
473
474 Example:
475 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
476 => { a = ["x" "y"]; b = ["z"] }
477 */
478 zipAttrsWith =
479 builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
480 /* Like `zipAttrsWith' with `(name: values: values)' as the function.
481
482 Example:
483 zipAttrs [{a = "x";} {a = "y"; b = "z";}]
484 => { a = ["x" "y"]; b = ["z"] }
485 */
486 zipAttrs = zipAttrsWith (name: values: values);
487
488 /* Does the same as the update operator '//' except that attributes are
489 merged until the given predicate is verified. The predicate should
490 accept 3 arguments which are the path to reach the attribute, a part of
491 the first attribute set and a part of the second attribute set. When
492 the predicate is verified, the value of the first attribute set is
493 replaced by the value of the second attribute set.
494
495 Example:
496 recursiveUpdateUntil (path: l: r: path == ["foo"]) {
497 # first attribute set
498 foo.bar = 1;
499 foo.baz = 2;
500 bar = 3;
501 } {
502 #second attribute set
503 foo.bar = 1;
504 foo.quz = 2;
505 baz = 4;
506 }
507
508 returns: {
509 foo.bar = 1; # 'foo.*' from the second set
510 foo.quz = 2; #
511 bar = 3; # 'bar' from the first set
512 baz = 4; # 'baz' from the second set
513 }
514
515 */
516 recursiveUpdateUntil = pred: lhs: rhs:
517 let f = attrPath:
518 zipAttrsWith (n: values:
519 let here = attrPath ++ [n]; in
520 if length values == 1
521 || pred here (elemAt values 1) (head values) then
522 head values
523 else
524 f here values
525 );
526 in f [] [rhs lhs];
527
528 /* A recursive variant of the update operator ‘//’. The recursion
529 stops when one of the attribute values is not an attribute set,
530 in which case the right hand side value takes precedence over the
531 left hand side value.
532
533 Example:
534 recursiveUpdate {
535 boot.loader.grub.enable = true;
536 boot.loader.grub.device = "/dev/hda";
537 } {
538 boot.loader.grub.device = "";
539 }
540
541 returns: {
542 boot.loader.grub.enable = true;
543 boot.loader.grub.device = "";
544 }
545
546 */
547 recursiveUpdate = recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs));
548
549 /* Returns true if the pattern is contained in the set. False otherwise.
550
551 Example:
552 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
553 => true
554 */
555 matchAttrs = pattern: attrs: assert isAttrs pattern;
556 all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
557 let pat = head values; val = elemAt values 1; in
558 if length values == 1 then false
559 else if isAttrs pat then isAttrs val && matchAttrs pat val
560 else pat == val
561 ) [pattern attrs]));
562
563 /* Override only the attributes that are already present in the old set
564 useful for deep-overriding.
565
566 Example:
567 overrideExisting {} { a = 1; }
568 => {}
569 overrideExisting { b = 2; } { a = 1; }
570 => { b = 2; }
571 overrideExisting { a = 3; b = 2; } { a = 1; }
572 => { a = 1; b = 2; }
573 */
574 overrideExisting = old: new:
575 mapAttrs (name: value: new.${name} or value) old;
576
577 /* Turns a list of strings into a human-readable description of those
578 strings represented as an attribute path. The result of this function is
579 not intended to be machine-readable.
580
581 Example:
582 showAttrPath [ "foo" "10" "bar" ]
583 => "foo.\"10\".bar"
584 showAttrPath []
585 => "<root attribute path>"
586 */
587 showAttrPath = path:
588 if path == [] then "<root attribute path>"
589 else concatMapStringsSep "." escapeNixIdentifier path;
590
591 /* Get a package output.
592 If no output is found, fallback to `.out` and then to the default.
593
594 Example:
595 getOutput "dev" pkgs.openssl
596 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev"
597 */
598 getOutput = output: pkg:
599 if ! pkg ? outputSpecified || ! pkg.outputSpecified
600 then pkg.${output} or pkg.out or pkg
601 else pkg;
602
603 getBin = getOutput "bin";
604 getLib = getOutput "lib";
605 getDev = getOutput "dev";
606 getMan = getOutput "man";
607
608 /* Pick the outputs of packages to place in buildInputs */
609 chooseDevOutputs = drvs: builtins.map getDev drvs;
610
611 /* Make various Nix tools consider the contents of the resulting
612 attribute set when looking for what to build, find, etc.
613
614 This function only affects a single attribute set; it does not
615 apply itself recursively for nested attribute sets.
616 */
617 recurseIntoAttrs =
618 attrs: attrs // { recurseForDerivations = true; };
619
620 /* Undo the effect of recurseIntoAttrs.
621 */
622 dontRecurseIntoAttrs =
623 attrs: attrs // { recurseForDerivations = false; };
624
625 /*** deprecated stuff ***/
626
627 zipWithNames = zipAttrsWithNames;
628 zip = builtins.trace
629 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
630}