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