at v206 11 kB view raw
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}