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