at 18.09-beta 14 kB view raw
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}