1# Operations on attribute sets.
2
3with {
4 inherit (builtins) head tail length;
5 inherit (import ./trivial.nix) or;
6 inherit (import ./default.nix) fold;
7 inherit (import ./strings.nix) concatStringsSep;
8 inherit (import ./lists.nix) concatMap concatLists all deepSeqList;
9};
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 recursivelly 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 // (listToAttrs [{inherit name; value = 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 = f: set:
199 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set));
200
201
202 /* Like `mapAttrs', but allows the name of each attribute to be
203 changed in addition to the value. The applied function should
204 return both the new name and value as a `nameValuePair'.
205
206 Example:
207 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
208 { x = "a"; y = "b"; }
209 => { foo_x = "bar-a"; foo_y = "bar-b"; }
210 */
211 mapAttrs' = f: set:
212 listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
213
214
215 /* Call a function for each attribute in the given set and return
216 the result in a list.
217
218 Example:
219 mapAttrsToList (name: value: name + value)
220 { x = "a"; y = "b"; }
221 => [ "xa" "yb" ]
222 */
223 mapAttrsToList = f: attrs:
224 map (name: f name attrs.${name}) (attrNames attrs);
225
226
227 /* Like `mapAttrs', except that it recursively applies itself to
228 attribute sets. Also, the first argument of the argument
229 function is a *list* of the names of the containing attributes.
230
231 Type:
232 mapAttrsRecursive ::
233 ([String] -> a -> b) -> AttrSet -> AttrSet
234
235 Example:
236 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
237 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
238 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
239 */
240 mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
241
242
243 /* Like `mapAttrsRecursive', but it takes an additional predicate
244 function that tells it whether to recursive into an attribute
245 set. If it returns false, `mapAttrsRecursiveCond' does not
246 recurse, but does apply the map function. It is returns true, it
247 does recurse, and does not apply the map function.
248
249 Type:
250 mapAttrsRecursiveCond ::
251 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
252
253 Example:
254 # To prevent recursing into derivations (which are attribute
255 # sets with the attribute "type" equal to "derivation"):
256 mapAttrsRecursiveCond
257 (as: !(as ? "type" && as.type == "derivation"))
258 (x: ... do something ...)
259 attrs
260 */
261 mapAttrsRecursiveCond = cond: f: set:
262 let
263 recurse = path: set:
264 let
265 g =
266 name: value:
267 if isAttrs value && cond value
268 then recurse (path ++ [name]) value
269 else f (path ++ [name]) value;
270 in mapAttrs g set;
271 in recurse [] set;
272
273
274 /* Generate an attribute set by mapping a function over a list of
275 attribute names.
276
277 Example:
278 genAttrs [ "foo" "bar" ] (name: "x_" + name)
279 => { foo = "x_foo"; bar = "x_bar"; }
280 */
281 genAttrs = names: f:
282 listToAttrs (map (n: nameValuePair n (f n)) names);
283
284
285 /* Check whether the argument is a derivation. Any set with
286 { type = "derivation"; } counts as a derivation.
287
288 Example:
289 nixpkgs = import <nixpkgs> {}
290 isDerivation nixpkgs.ruby
291 => true
292 isDerivation "foobar"
293 => false
294 */
295 isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
296
297 /* Converts a store path to a fake derivation. */
298 toDerivation = path:
299 let path' = builtins.storePath path; in
300 { type = "derivation";
301 name = builtins.unsafeDiscardStringContext (builtins.substring 33 (-1) (baseNameOf path'));
302 outPath = path';
303 outputs = [ "out" ];
304 };
305
306
307 /* If `cond' is true, return the attribute set `as',
308 otherwise an empty attribute set.
309
310 Example:
311 optionalAttrs (true) { my = "set"; }
312 => { my = "set"; }
313 optionalAttrs (false) { my = "set"; }
314 => { }
315 */
316 optionalAttrs = cond: as: if cond then as else {};
317
318
319 /* Merge sets of attributes and use the function f to merge attributes
320 values.
321
322 Example:
323 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
324 => { a = ["x" "y"]; }
325 */
326 zipAttrsWithNames = names: f: sets:
327 listToAttrs (map (name: {
328 inherit name;
329 value = f name (catAttrs name sets);
330 }) names);
331
332 /* Implentation note: Common names appear multiple times in the list of
333 names, hopefully this does not affect the system because the maximal
334 laziness avoid computing twice the same expression and listToAttrs does
335 not care about duplicated attribute names.
336
337 Example:
338 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
339 => { a = ["x" "y"]; b = ["z"] }
340 */
341 zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
342 /* Like `zipAttrsWith' with `(name: values: value)' as the function.
343
344 Example:
345 zipAttrs [{a = "x";} {a = "y"; b = "z";}]
346 => { a = ["x" "y"]; b = ["z"] }
347 */
348 zipAttrs = zipAttrsWith (name: values: values);
349
350 /* Does the same as the update operator '//' except that attributes are
351 merged until the given pedicate is verified. The predicate should
352 accept 3 arguments which are the path to reach the attribute, a part of
353 the first attribute set and a part of the second attribute set. When
354 the predicate is verified, the value of the first attribute set is
355 replaced by the value of the second attribute set.
356
357 Example:
358 recursiveUpdateUntil (path: l: r: path == ["foo"]) {
359 # first attribute set
360 foo.bar = 1;
361 foo.baz = 2;
362 bar = 3;
363 } {
364 #second attribute set
365 foo.bar = 1;
366 foo.quz = 2;
367 baz = 4;
368 }
369
370 returns: {
371 foo.bar = 1; # 'foo.*' from the second set
372 foo.quz = 2; #
373 bar = 3; # 'bar' from the first set
374 baz = 4; # 'baz' from the second set
375 }
376
377 */
378 recursiveUpdateUntil = pred: lhs: rhs:
379 let f = attrPath:
380 zipAttrsWith (n: values:
381 if tail values == []
382 || pred attrPath (head (tail values)) (head values) then
383 head values
384 else
385 f (attrPath ++ [n]) values
386 );
387 in f [] [rhs lhs];
388
389 /* A recursive variant of the update operator ‘//’. The recusion
390 stops when one of the attribute values is not an attribute set,
391 in which case the right hand side value takes precedence over the
392 left hand side value.
393
394 Example:
395 recursiveUpdate {
396 boot.loader.grub.enable = true;
397 boot.loader.grub.device = "/dev/hda";
398 } {
399 boot.loader.grub.device = "";
400 }
401
402 returns: {
403 boot.loader.grub.enable = true;
404 boot.loader.grub.device = "";
405 }
406
407 */
408 recursiveUpdate = lhs: rhs:
409 recursiveUpdateUntil (path: lhs: rhs:
410 !(isAttrs lhs && isAttrs rhs)
411 ) lhs rhs;
412
413 /* Returns true if the pattern is contained in the set. False otherwise.
414
415 FIXME(zimbatm): this example doesn't work !!!
416
417 Example:
418 sys = mkSystem { }
419 matchAttrs { cpu = { bits = 64; }; } sys
420 => true
421 */
422 matchAttrs = pattern: attrs:
423 fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
424 let pat = head values; val = head (tail values); in
425 if length values == 1 then false
426 else if isAttrs pat then isAttrs val && matchAttrs head values
427 else pat == val
428 ) [pattern attrs]));
429
430 /* Override only the attributes that are already present in the old set
431 useful for deep-overriding.
432
433 Example:
434 x = { a = { b = 4; c = 3; }; }
435 overrideExisting x { a = { b = 6; d = 2; }; }
436 => { a = { b = 6; d = 2; }; }
437 */
438 overrideExisting = old: new:
439 old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old));
440
441 /* Get a package output.
442 If no output is found, fallback to `.out` and then to the default.
443
444 Example:
445 getOutput "dev" pkgs.openssl
446 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev"
447 */
448 getOutput = output: pkg:
449 if pkg.outputUnspecified or false
450 then pkg.${output} or pkg.out or pkg
451 else pkg;
452
453 getBin = getOutput "bin";
454 getLib = getOutput "lib";
455 getDev = getOutput "dev";
456
457 /* Pick the outputs of packages to place in buildInputs */
458 chooseDevOutputs = drvs: builtins.map (drv: if drv.outputUnspecified or false then drv.dev or drv else drv) drvs;
459
460 /*** deprecated stuff ***/
461
462 zipWithNames = zipAttrsWithNames;
463 zip = builtins.trace
464 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
465
466}