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