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. For instance
16 ["x" "y"] applied to some set e returns e.x.y, if it exists. The
17 default value is returned otherwise. */
18 attrByPath = attrPath: default: e:
19 let attr = head attrPath;
20 in
21 if attrPath == [] then e
22 else if e ? ${attr}
23 then attrByPath (tail attrPath) default e.${attr}
24 else default;
25
26
27 /* Return nested attribute set in which an attribute is set. For instance
28 ["x" "y"] applied with some value v returns `x.y = v;' */
29 setAttrByPath = attrPath: value:
30 if attrPath == [] then value
31 else listToAttrs
32 [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ];
33
34
35 getAttrFromPath = attrPath: set:
36 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
37 in attrByPath attrPath (abort errorMsg) set;
38
39
40 /* Return the specified attributes from a set.
41
42 Example:
43 attrVals ["a" "b" "c"] as
44 => [as.a as.b as.c]
45 */
46 attrVals = nameList: set: map (x: set.${x}) nameList;
47
48
49 /* Return the values of all attributes in the given set, sorted by
50 attribute name.
51
52 Example:
53 attrValues {c = 3; a = 1; b = 2;}
54 => [1 2 3]
55 */
56 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs);
57
58
59 /* Collect each attribute named `attr' from a list of attribute
60 sets. Sets that don't contain the named attribute are ignored.
61
62 Example:
63 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
64 => [1 2]
65 */
66 catAttrs = builtins.catAttrs or
67 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l));
68
69
70 /* Filter an attribute set by removing all attributes for which the
71 given predicate return false.
72
73 Example:
74 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
75 => { foo = 1; }
76 */
77 filterAttrs = pred: set:
78 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
79
80
81 /* Filter an attribute set recursivelly by removing all attributes for
82 which the given predicate return false.
83
84 Example:
85 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; }
86 => { foo = {}; }
87 */
88 filterAttrsRecursive = pred: set:
89 listToAttrs (
90 concatMap (name:
91 let v = set.${name}; in
92 if pred name v then [
93 (nameValuePair name (
94 if isAttrs v then filterAttrsRecursive pred v
95 else v
96 ))
97 ] else []
98 ) (attrNames set)
99 );
100
101 /* foldAttrs: apply fold functions to values grouped by key. Eg accumulate values as list:
102 foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
103 => { a = [ 2 3 ]; }
104 */
105 foldAttrs = op: nul: list_of_attrs:
106 fold (n: a:
107 fold (name: o:
108 o // (listToAttrs [{inherit name; value = op n.${name} (a.${name} or nul); }])
109 ) a (attrNames n)
110 ) {} list_of_attrs;
111
112
113 /* Recursively collect sets that verify a given predicate named `pred'
114 from the set `attrs'. The recursion is stopped when the predicate is
115 verified.
116
117 Type:
118 collect ::
119 (AttrSet -> Bool) -> AttrSet -> AttrSet
120
121 Example:
122 collect isList { a = { b = ["b"]; }; c = [1]; }
123 => [["b"] [1]]
124
125 collect (x: x ? outPath)
126 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
127 => [{ outPath = "a/"; } { outPath = "b/"; }]
128 */
129 collect = pred: attrs:
130 if pred attrs then
131 [ attrs ]
132 else if isAttrs attrs then
133 concatMap (collect pred) (attrValues attrs)
134 else
135 [];
136
137
138 /* Utility function that creates a {name, value} pair as expected by
139 builtins.listToAttrs. */
140 nameValuePair = name: value: { inherit name value; };
141
142
143 /* Apply a function to each element in an attribute set. The
144 function takes two arguments --- the attribute name and its value
145 --- and returns the new value for the attribute. The result is a
146 new attribute set.
147
148 Example:
149 mapAttrs (name: value: name + "-" + value)
150 { x = "foo"; y = "bar"; }
151 => { x = "x-foo"; y = "y-bar"; }
152 */
153 mapAttrs = f: set:
154 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set));
155
156
157 /* Like `mapAttrs', but allows the name of each attribute to be
158 changed in addition to the value. The applied function should
159 return both the new name and value as a `nameValuePair'.
160
161 Example:
162 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
163 { x = "a"; y = "b"; }
164 => { foo_x = "bar-a"; foo_y = "bar-b"; }
165 */
166 mapAttrs' = f: set:
167 listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
168
169
170 /* Call a function for each attribute in the given set and return
171 the result in a list.
172
173 Example:
174 mapAttrsToList (name: value: name + value)
175 { x = "a"; y = "b"; }
176 => [ "xa" "yb" ]
177 */
178 mapAttrsToList = f: attrs:
179 map (name: f name attrs.${name}) (attrNames attrs);
180
181
182 /* Like `mapAttrs', except that it recursively applies itself to
183 attribute sets. Also, the first argument of the argument
184 function is a *list* of the names of the containing attributes.
185
186 Type:
187 mapAttrsRecursive ::
188 ([String] -> a -> b) -> AttrSet -> AttrSet
189
190 Example:
191 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
192 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
193 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
194 */
195 mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
196
197
198 /* Like `mapAttrsRecursive', but it takes an additional predicate
199 function that tells it whether to recursive into an attribute
200 set. If it returns false, `mapAttrsRecursiveCond' does not
201 recurse, but does apply the map function. It is returns true, it
202 does recurse, and does not apply the map function.
203
204 Type:
205 mapAttrsRecursiveCond ::
206 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
207
208 Example:
209 # To prevent recursing into derivations (which are attribute
210 # sets with the attribute "type" equal to "derivation"):
211 mapAttrsRecursiveCond
212 (as: !(as ? "type" && as.type == "derivation"))
213 (x: ... do something ...)
214 attrs
215 */
216 mapAttrsRecursiveCond = cond: f: set:
217 let
218 recurse = path: set:
219 let
220 g =
221 name: value:
222 if isAttrs value && cond value
223 then recurse (path ++ [name]) value
224 else f (path ++ [name]) value;
225 in mapAttrs g set;
226 in recurse [] set;
227
228
229 /* Generate an attribute set by mapping a function over a list of
230 attribute names.
231
232 Example:
233 genAttrs [ "foo" "bar" ] (name: "x_" + name)
234 => { foo = "x_foo"; bar = "x_bar"; }
235 */
236 genAttrs = names: f:
237 listToAttrs (map (n: nameValuePair n (f n)) names);
238
239
240 /* Check whether the argument is a derivation. */
241 isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
242
243
244 /* Convert a store path to a fake derivation. */
245 toDerivation = path:
246 let path' = builtins.storePath path; in
247 { type = "derivation";
248 name = builtins.unsafeDiscardStringContext (builtins.substring 33 (-1) (baseNameOf path'));
249 outPath = path';
250 outputs = [ "out" ];
251 };
252
253
254 /* If the Boolean `cond' is true, return the attribute set `as',
255 otherwise an empty attribute set. */
256 optionalAttrs = cond: as: if cond then as else {};
257
258
259 /* Merge sets of attributes and use the function f to merge attributes
260 values. */
261 zipAttrsWithNames = names: f: sets:
262 listToAttrs (map (name: {
263 inherit name;
264 value = f name (catAttrs name sets);
265 }) names);
266
267 # implentation note: Common names appear multiple times in the list of
268 # names, hopefully this does not affect the system because the maximal
269 # laziness avoid computing twice the same expression and listToAttrs does
270 # not care about duplicated attribute names.
271 zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
272
273 zipAttrs = zipAttrsWith (name: values: values);
274
275 /* backward compatibility */
276 zipWithNames = zipAttrsWithNames;
277 zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
278
279
280 /* Does the same as the update operator '//' except that attributes are
281 merged until the given pedicate is verified. The predicate should
282 accept 3 arguments which are the path to reach the attribute, a part of
283 the first attribute set and a part of the second attribute set. When
284 the predicate is verified, the value of the first attribute set is
285 replaced by the value of the second attribute set.
286
287 Example:
288 recursiveUpdateUntil (path: l: r: path == ["foo"]) {
289 # first attribute set
290 foo.bar = 1;
291 foo.baz = 2;
292 bar = 3;
293 } {
294 #second attribute set
295 foo.bar = 1;
296 foo.quz = 2;
297 baz = 4;
298 }
299
300 returns: {
301 foo.bar = 1; # 'foo.*' from the second set
302 foo.quz = 2; #
303 bar = 3; # 'bar' from the first set
304 baz = 4; # 'baz' from the second set
305 }
306
307 */
308 recursiveUpdateUntil = pred: lhs: rhs:
309 let f = attrPath:
310 zipAttrsWith (n: values:
311 if tail values == []
312 || pred attrPath (head (tail values)) (head values) then
313 head values
314 else
315 f (attrPath ++ [n]) values
316 );
317 in f [] [rhs lhs];
318
319 /* A recursive variant of the update operator ‘//’. The recusion
320 stops when one of the attribute values is not an attribute set,
321 in which case the right hand side value takes precedence over the
322 left hand side value.
323
324 Example:
325 recursiveUpdate {
326 boot.loader.grub.enable = true;
327 boot.loader.grub.device = "/dev/hda";
328 } {
329 boot.loader.grub.device = "";
330 }
331
332 returns: {
333 boot.loader.grub.enable = true;
334 boot.loader.grub.device = "";
335 }
336
337 */
338 recursiveUpdate = lhs: rhs:
339 recursiveUpdateUntil (path: lhs: rhs:
340 !(isAttrs lhs && isAttrs rhs)
341 ) lhs rhs;
342
343 matchAttrs = pattern: attrs:
344 fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
345 let pat = head values; val = head (tail values); in
346 if length values == 1 then false
347 else if isAttrs pat then isAttrs val && matchAttrs head values
348 else pat == val
349 ) [pattern attrs]));
350
351 # override only the attributes that are already present in the old set
352 # useful for deep-overriding
353 overrideExisting = old: new:
354 old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old));
355
356 deepSeqAttrs = x: y: deepSeqList (attrValues x) y;
357}