1{ lib }:
2# Operations on attribute sets.
3
4let
5 inherit (builtins) head tail length;
6 inherit (lib.trivial) and;
7 inherit (lib.strings) concatStringsSep sanitizeDerivationName;
8 inherit (lib.lists) foldr foldl' concatMap concatLists elemAt;
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: set:
77 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
78 in attrByPath attrPath (abort errorMsg) set;
79
80
81 /* Return the specified attributes from a set.
82
83 Example:
84 attrVals ["a" "b" "c"] as
85 => [as.a as.b as.c]
86 */
87 attrVals = nameList: set: map (x: set.${x}) nameList;
88
89
90 /* Return the values of all attributes in the given set, sorted by
91 attribute name.
92
93 Example:
94 attrValues {c = 3; a = 1; b = 2;}
95 => [1 2 3]
96 */
97 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs);
98
99
100 /* Given a set of attribute names, return the set of the corresponding
101 attributes from the given set.
102
103 Example:
104 getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; }
105 => { a = 1; b = 2; }
106 */
107 getAttrs = names: attrs: genAttrs names (name: attrs.${name});
108
109 /* Collect each attribute named `attr' from a list of attribute
110 sets. Sets that don't contain the named attribute are ignored.
111
112 Example:
113 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
114 => [1 2]
115 */
116 catAttrs = builtins.catAttrs or
117 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l));
118
119
120 /* Filter an attribute set by removing all attributes for which the
121 given predicate return false.
122
123 Example:
124 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
125 => { foo = 1; }
126 */
127 filterAttrs = pred: set:
128 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
129
130
131 /* Filter an attribute set recursively by removing all attributes for
132 which the given predicate return false.
133
134 Example:
135 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; }
136 => { foo = {}; }
137 */
138 filterAttrsRecursive = pred: set:
139 listToAttrs (
140 concatMap (name:
141 let v = set.${name}; in
142 if pred name v then [
143 (nameValuePair name (
144 if isAttrs v then filterAttrsRecursive pred v
145 else v
146 ))
147 ] else []
148 ) (attrNames set)
149 );
150
151 /* Apply fold functions to values grouped by key.
152
153 Example:
154 foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
155 => { a = [ 2 3 ]; }
156 */
157 foldAttrs = op: nul: list_of_attrs:
158 foldr (n: a:
159 foldr (name: o:
160 o // { ${name} = op n.${name} (a.${name} or nul); }
161 ) a (attrNames n)
162 ) {} list_of_attrs;
163
164
165 /* Recursively collect sets that verify a given predicate named `pred'
166 from the set `attrs'. The recursion is stopped when the predicate is
167 verified.
168
169 Type:
170 collect ::
171 (AttrSet -> Bool) -> AttrSet -> [x]
172
173 Example:
174 collect isList { a = { b = ["b"]; }; c = [1]; }
175 => [["b"] [1]]
176
177 collect (x: x ? outPath)
178 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
179 => [{ outPath = "a/"; } { outPath = "b/"; }]
180 */
181 collect = pred: attrs:
182 if pred attrs then
183 [ attrs ]
184 else if isAttrs attrs then
185 concatMap (collect pred) (attrValues attrs)
186 else
187 [];
188
189 /* Return the cartesian product of attribute set value combinations.
190
191 Example:
192 cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
193 => [
194 { a = 1; b = 10; }
195 { a = 1; b = 20; }
196 { a = 2; b = 10; }
197 { a = 2; b = 20; }
198 ]
199 */
200 cartesianProductOfSets = attrsOfLists:
201 foldl' (listOfAttrs: attrName:
202 concatMap (attrs:
203 map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
204 ) listOfAttrs
205 ) [{}] (attrNames attrsOfLists);
206
207
208 /* Utility function that creates a {name, value} pair as expected by
209 builtins.listToAttrs.
210
211 Example:
212 nameValuePair "some" 6
213 => { name = "some"; value = 6; }
214 */
215 nameValuePair = name: value: { inherit name value; };
216
217
218 /* Apply a function to each element in an attribute set. The
219 function takes two arguments --- the attribute name and its value
220 --- and returns the new value for the attribute. The result is a
221 new attribute set.
222
223 Example:
224 mapAttrs (name: value: name + "-" + value)
225 { x = "foo"; y = "bar"; }
226 => { x = "x-foo"; y = "y-bar"; }
227 */
228 mapAttrs = builtins.mapAttrs or
229 (f: set:
230 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));
231
232
233 /* Like `mapAttrs', but allows the name of each attribute to be
234 changed in addition to the value. The applied function should
235 return both the new name and value as a `nameValuePair'.
236
237 Example:
238 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
239 { x = "a"; y = "b"; }
240 => { foo_x = "bar-a"; foo_y = "bar-b"; }
241 */
242 mapAttrs' = f: set:
243 listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
244
245
246 /* Call a function for each attribute in the given set and return
247 the result in a list.
248
249 Type:
250 mapAttrsToList ::
251 (String -> a -> b) -> AttrSet -> [b]
252
253 Example:
254 mapAttrsToList (name: value: name + value)
255 { x = "a"; y = "b"; }
256 => [ "xa" "yb" ]
257 */
258 mapAttrsToList = f: attrs:
259 map (name: f name attrs.${name}) (attrNames attrs);
260
261
262 /* Like `mapAttrs', except that it recursively applies itself to
263 attribute sets. Also, the first argument of the argument
264 function is a *list* of the names of the containing attributes.
265
266 Type:
267 mapAttrsRecursive ::
268 ([String] -> a -> b) -> AttrSet -> AttrSet
269
270 Example:
271 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
272 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
273 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
274 */
275 mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
276
277
278 /* Like `mapAttrsRecursive', but it takes an additional predicate
279 function that tells it whether to recursive into an attribute
280 set. If it returns false, `mapAttrsRecursiveCond' does not
281 recurse, but does apply the map function. If it returns true, it
282 does recurse, and does not apply the map function.
283
284 Type:
285 mapAttrsRecursiveCond ::
286 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
287
288 Example:
289 # To prevent recursing into derivations (which are attribute
290 # sets with the attribute "type" equal to "derivation"):
291 mapAttrsRecursiveCond
292 (as: !(as ? "type" && as.type == "derivation"))
293 (x: ... do something ...)
294 attrs
295 */
296 mapAttrsRecursiveCond = cond: f: set:
297 let
298 recurse = path: set:
299 let
300 g =
301 name: value:
302 if isAttrs value && cond value
303 then recurse (path ++ [name]) value
304 else f (path ++ [name]) value;
305 in mapAttrs g set;
306 in recurse [] set;
307
308
309 /* Generate an attribute set by mapping a function over a list of
310 attribute names.
311
312 Example:
313 genAttrs [ "foo" "bar" ] (name: "x_" + name)
314 => { foo = "x_foo"; bar = "x_bar"; }
315 */
316 genAttrs = names: f:
317 listToAttrs (map (n: nameValuePair n (f n)) names);
318
319
320 /* Check whether the argument is a derivation. Any set with
321 { type = "derivation"; } counts as a derivation.
322
323 Example:
324 nixpkgs = import <nixpkgs> {}
325 isDerivation nixpkgs.ruby
326 => true
327 isDerivation "foobar"
328 => false
329 */
330 isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
331
332 /* Converts a store path to a fake derivation. */
333 toDerivation = path:
334 let
335 path' = builtins.storePath path;
336 res =
337 { type = "derivation";
338 name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
339 outPath = path';
340 outputs = [ "out" ];
341 out = res;
342 outputName = "out";
343 };
344 in res;
345
346
347 /* If `cond' is true, return the attribute set `as',
348 otherwise an empty attribute set.
349
350 Example:
351 optionalAttrs (true) { my = "set"; }
352 => { my = "set"; }
353 optionalAttrs (false) { my = "set"; }
354 => { }
355 */
356 optionalAttrs = cond: as: if cond then as else {};
357
358
359 /* Merge sets of attributes and use the function f to merge attributes
360 values.
361
362 Example:
363 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
364 => { a = ["x" "y"]; }
365 */
366 zipAttrsWithNames = names: f: sets:
367 listToAttrs (map (name: {
368 inherit name;
369 value = f name (catAttrs name sets);
370 }) names);
371
372 /* Implementation note: Common names appear multiple times in the list of
373 names, hopefully this does not affect the system because the maximal
374 laziness avoid computing twice the same expression and listToAttrs does
375 not care about duplicated attribute names.
376
377 Example:
378 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
379 => { a = ["x" "y"]; b = ["z"] }
380 */
381 zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
382 /* Like `zipAttrsWith' with `(name: values: values)' as the function.
383
384 Example:
385 zipAttrs [{a = "x";} {a = "y"; b = "z";}]
386 => { a = ["x" "y"]; b = ["z"] }
387 */
388 zipAttrs = zipAttrsWith (name: values: values);
389
390 /* Does the same as the update operator '//' except that attributes are
391 merged until the given predicate is verified. The predicate should
392 accept 3 arguments which are the path to reach the attribute, a part of
393 the first attribute set and a part of the second attribute set. When
394 the predicate is verified, the value of the first attribute set is
395 replaced by the value of the second attribute set.
396
397 Example:
398 recursiveUpdateUntil (path: l: r: path == ["foo"]) {
399 # first attribute set
400 foo.bar = 1;
401 foo.baz = 2;
402 bar = 3;
403 } {
404 #second attribute set
405 foo.bar = 1;
406 foo.quz = 2;
407 baz = 4;
408 }
409
410 returns: {
411 foo.bar = 1; # 'foo.*' from the second set
412 foo.quz = 2; #
413 bar = 3; # 'bar' from the first set
414 baz = 4; # 'baz' from the second set
415 }
416
417 */
418 recursiveUpdateUntil = pred: lhs: rhs:
419 let f = attrPath:
420 zipAttrsWith (n: values:
421 let here = attrPath ++ [n]; in
422 if tail values == []
423 || pred here (head (tail values)) (head values) then
424 head values
425 else
426 f here values
427 );
428 in f [] [rhs lhs];
429
430 /* A recursive variant of the update operator ‘//’. The recursion
431 stops when one of the attribute values is not an attribute set,
432 in which case the right hand side value takes precedence over the
433 left hand side value.
434
435 Example:
436 recursiveUpdate {
437 boot.loader.grub.enable = true;
438 boot.loader.grub.device = "/dev/hda";
439 } {
440 boot.loader.grub.device = "";
441 }
442
443 returns: {
444 boot.loader.grub.enable = true;
445 boot.loader.grub.device = "";
446 }
447
448 */
449 recursiveUpdate = lhs: rhs:
450 recursiveUpdateUntil (path: lhs: rhs:
451 !(isAttrs lhs && isAttrs rhs)
452 ) lhs rhs;
453
454 /* Returns true if the pattern is contained in the set. False otherwise.
455
456 Example:
457 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
458 => true
459 */
460 matchAttrs = pattern: attrs: assert isAttrs pattern;
461 foldr and true (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
462 let pat = head values; val = head (tail values); in
463 if length values == 1 then false
464 else if isAttrs pat then isAttrs val && matchAttrs pat val
465 else pat == val
466 ) [pattern attrs]));
467
468 /* Override only the attributes that are already present in the old set
469 useful for deep-overriding.
470
471 Example:
472 overrideExisting {} { a = 1; }
473 => {}
474 overrideExisting { b = 2; } { a = 1; }
475 => { b = 2; }
476 overrideExisting { a = 3; b = 2; } { a = 1; }
477 => { a = 1; b = 2; }
478 */
479 overrideExisting = old: new:
480 mapAttrs (name: value: new.${name} or value) old;
481
482 /* Get a package output.
483 If no output is found, fallback to `.out` and then to the default.
484
485 Example:
486 getOutput "dev" pkgs.openssl
487 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev"
488 */
489 getOutput = output: pkg:
490 if ! pkg ? outputSpecified || ! pkg.outputSpecified
491 then pkg.${output} or pkg.out or pkg
492 else pkg;
493
494 getBin = getOutput "bin";
495 getLib = getOutput "lib";
496 getDev = getOutput "dev";
497 getMan = getOutput "man";
498
499 /* Pick the outputs of packages to place in buildInputs */
500 chooseDevOutputs = drvs: builtins.map getDev drvs;
501
502 /* Make various Nix tools consider the contents of the resulting
503 attribute set when looking for what to build, find, etc.
504
505 This function only affects a single attribute set; it does not
506 apply itself recursively for nested attribute sets.
507 */
508 recurseIntoAttrs =
509 attrs: attrs // { recurseForDerivations = true; };
510
511 /* Undo the effect of recurseIntoAttrs.
512 */
513 dontRecurseIntoAttrs =
514 attrs: attrs // { recurseForDerivations = false; };
515
516 /*** deprecated stuff ***/
517
518 zipWithNames = zipAttrsWithNames;
519 zip = builtins.trace
520 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
521}