at 24.05-pre 33 kB view raw
1/* Operations on attribute sets. */ 2{ lib }: 3 4let 5 inherit (builtins) head tail length; 6 inherit (lib.trivial) id mergeAttrs; 7 inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName; 8 inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl; 9in 10 11rec { 12 inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs; 13 14 15 /* Return an attribute from nested attribute sets. 16 17 Example: 18 x = { a = { b = 3; }; } 19 # ["a" "b"] is equivalent to x.a.b 20 # 6 is a default value to return if the path does not exist in attrset 21 attrByPath ["a" "b"] 6 x 22 => 3 23 attrByPath ["z" "z"] 6 x 24 => 6 25 26 Type: 27 attrByPath :: [String] -> Any -> AttrSet -> Any 28 29 */ 30 attrByPath = 31 # A list of strings representing the attribute path to return from `set` 32 attrPath: 33 # Default value if `attrPath` does not resolve to an existing value 34 default: 35 # The nested attribute set to select values from 36 set: 37 let attr = head attrPath; 38 in 39 if attrPath == [] then set 40 else if set ? ${attr} 41 then attrByPath (tail attrPath) default set.${attr} 42 else default; 43 44 /* Return if an attribute from nested attribute set exists. 45 46 Example: 47 x = { a = { b = 3; }; } 48 hasAttrByPath ["a" "b"] x 49 => true 50 hasAttrByPath ["z" "z"] x 51 => false 52 53 Type: 54 hasAttrByPath :: [String] -> AttrSet -> Bool 55 */ 56 hasAttrByPath = 57 # A list of strings representing the attribute path to check from `set` 58 attrPath: 59 # The nested attribute set to check 60 e: 61 let attr = head attrPath; 62 in 63 if attrPath == [] then true 64 else if e ? ${attr} 65 then hasAttrByPath (tail attrPath) e.${attr} 66 else false; 67 68 69 /* Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`. 70 71 Example: 72 setAttrByPath ["a" "b"] 3 73 => { a = { b = 3; }; } 74 75 Type: 76 setAttrByPath :: [String] -> Any -> AttrSet 77 */ 78 setAttrByPath = 79 # A list of strings representing the attribute path to set 80 attrPath: 81 # The value to set at the location described by `attrPath` 82 value: 83 let 84 len = length attrPath; 85 atDepth = n: 86 if n == len 87 then value 88 else { ${elemAt attrPath n} = atDepth (n + 1); }; 89 in atDepth 0; 90 91 /* Like `attrByPath`, but without a default value. If it doesn't find the 92 path it will throw an error. 93 94 Example: 95 x = { a = { b = 3; }; } 96 getAttrFromPath ["a" "b"] x 97 => 3 98 getAttrFromPath ["z" "z"] x 99 => error: cannot find attribute `z.z' 100 101 Type: 102 getAttrFromPath :: [String] -> AttrSet -> Any 103 */ 104 getAttrFromPath = 105 # A list of strings representing the attribute path to get from `set` 106 attrPath: 107 # The nested attribute set to find the value in. 108 set: 109 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'"; 110 in attrByPath attrPath (abort errorMsg) set; 111 112 /* Map each attribute in the given set and merge them into a new attribute set. 113 114 Type: 115 concatMapAttrs :: (String -> a -> AttrSet) -> AttrSet -> AttrSet 116 117 Example: 118 concatMapAttrs 119 (name: value: { 120 ${name} = value; 121 ${name + value} = value; 122 }) 123 { x = "a"; y = "b"; } 124 => { x = "a"; xa = "a"; y = "b"; yb = "b"; } 125 */ 126 concatMapAttrs = f: v: 127 foldl' mergeAttrs { } 128 (attrValues 129 (mapAttrs f v) 130 ); 131 132 133 /* Update or set specific paths of an attribute set. 134 135 Takes a list of updates to apply and an attribute set to apply them to, 136 and returns the attribute set with the updates applied. Updates are 137 represented as `{ path = ...; update = ...; }` values, where `path` is a 138 list of strings representing the attribute path that should be updated, 139 and `update` is a function that takes the old value at that attribute path 140 as an argument and returns the new 141 value it should be. 142 143 Properties: 144 145 - Updates to deeper attribute paths are applied before updates to more 146 shallow attribute paths 147 148 - Multiple updates to the same attribute path are applied in the order 149 they appear in the update list 150 151 - If any but the last `path` element leads into a value that is not an 152 attribute set, an error is thrown 153 154 - If there is an update for an attribute path that doesn't exist, 155 accessing the argument in the update function causes an error, but 156 intermediate attribute sets are implicitly created as needed 157 158 Example: 159 updateManyAttrsByPath [ 160 { 161 path = [ "a" "b" ]; 162 update = old: { d = old.c; }; 163 } 164 { 165 path = [ "a" "b" "c" ]; 166 update = old: old + 1; 167 } 168 { 169 path = [ "x" "y" ]; 170 update = old: "xy"; 171 } 172 ] { a.b.c = 0; } 173 => { a = { b = { d = 1; }; }; x = { y = "xy"; }; } 174 175 Type: updateManyAttrsByPath :: [{ path :: [String]; update :: (Any -> Any); }] -> AttrSet -> AttrSet 176 */ 177 updateManyAttrsByPath = let 178 # When recursing into attributes, instead of updating the `path` of each 179 # update using `tail`, which needs to allocate an entirely new list, 180 # we just pass a prefix length to use and make sure to only look at the 181 # path without the prefix length, so that we can reuse the original list 182 # entries. 183 go = prefixLength: hasValue: value: updates: 184 let 185 # Splits updates into ones on this level (split.right) 186 # And ones on levels further down (split.wrong) 187 split = partition (el: length el.path == prefixLength) updates; 188 189 # Groups updates on further down levels into the attributes they modify 190 nested = groupBy (el: elemAt el.path prefixLength) split.wrong; 191 192 # Applies only nested modification to the input value 193 withNestedMods = 194 # Return the value directly if we don't have any nested modifications 195 if split.wrong == [] then 196 if hasValue then value 197 else 198 # Throw an error if there is no value. This `head` call here is 199 # safe, but only in this branch since `go` could only be called 200 # with `hasValue == false` for nested updates, in which case 201 # it's also always called with at least one update 202 let updatePath = (head split.right).path; in 203 throw 204 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does " 205 + "not exist in the given value, but the first update to this " 206 + "path tries to access the existing value.") 207 else 208 # If there are nested modifications, try to apply them to the value 209 if ! hasValue then 210 # But if we don't have a value, just use an empty attribute set 211 # as the value, but simplify the code a bit 212 mapAttrs (name: go (prefixLength + 1) false null) nested 213 else if isAttrs value then 214 # If we do have a value and it's an attribute set, override it 215 # with the nested modifications 216 value // 217 mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested 218 else 219 # However if it's not an attribute set, we can't apply the nested 220 # modifications, throw an error 221 let updatePath = (head split.wrong).path; in 222 throw 223 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to " 224 + "be updated, but path '${showAttrPath (take prefixLength updatePath)}' " 225 + "of the given value is not an attribute set, so we can't " 226 + "update an attribute inside of it."); 227 228 # We get the final result by applying all the updates on this level 229 # after having applied all the nested updates 230 # We use foldl instead of foldl' so that in case of multiple updates, 231 # intermediate values aren't evaluated if not needed 232 in foldl (acc: el: el.update acc) withNestedMods split.right; 233 234 in updates: value: go 0 true value updates; 235 236 /* Return the specified attributes from a set. 237 238 Example: 239 attrVals ["a" "b" "c"] as 240 => [as.a as.b as.c] 241 242 Type: 243 attrVals :: [String] -> AttrSet -> [Any] 244 */ 245 attrVals = 246 # The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set 247 nameList: 248 # The set to get attribute values from 249 set: map (x: set.${x}) nameList; 250 251 252 /* Return the values of all attributes in the given set, sorted by 253 attribute name. 254 255 Example: 256 attrValues {c = 3; a = 1; b = 2;} 257 => [1 2 3] 258 259 Type: 260 attrValues :: AttrSet -> [Any] 261 */ 262 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs); 263 264 265 /* Given a set of attribute names, return the set of the corresponding 266 attributes from the given set. 267 268 Example: 269 getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } 270 => { a = 1; b = 2; } 271 272 Type: 273 getAttrs :: [String] -> AttrSet -> AttrSet 274 */ 275 getAttrs = 276 # A list of attribute names to get out of `set` 277 names: 278 # The set to get the named attributes from 279 attrs: genAttrs names (name: attrs.${name}); 280 281 /* Collect each attribute named `attr` from a list of attribute 282 sets. Sets that don't contain the named attribute are ignored. 283 284 Example: 285 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] 286 => [1 2] 287 288 Type: 289 catAttrs :: String -> [AttrSet] -> [Any] 290 */ 291 catAttrs = builtins.catAttrs or 292 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l)); 293 294 295 /* Filter an attribute set by removing all attributes for which the 296 given predicate return false. 297 298 Example: 299 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } 300 => { foo = 1; } 301 302 Type: 303 filterAttrs :: (String -> Any -> Bool) -> AttrSet -> AttrSet 304 */ 305 filterAttrs = 306 # Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. 307 pred: 308 # The attribute set to filter 309 set: 310 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set)); 311 312 313 /* Filter an attribute set recursively by removing all attributes for 314 which the given predicate return false. 315 316 Example: 317 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } 318 => { foo = {}; } 319 320 Type: 321 filterAttrsRecursive :: (String -> Any -> Bool) -> AttrSet -> AttrSet 322 */ 323 filterAttrsRecursive = 324 # Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. 325 pred: 326 # The attribute set to filter 327 set: 328 listToAttrs ( 329 concatMap (name: 330 let v = set.${name}; in 331 if pred name v then [ 332 (nameValuePair name ( 333 if isAttrs v then filterAttrsRecursive pred v 334 else v 335 )) 336 ] else [] 337 ) (attrNames set) 338 ); 339 340 /* 341 Like [`lib.lists.foldl'`](#function-library-lib.lists.foldl-prime) but for attribute sets. 342 Iterates over every name-value pair in the given attribute set. 343 The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`. 344 345 Attention: 346 There is a completely different function 347 `lib.foldAttrs` 348 which has nothing to do with this function, despite the similar name. 349 350 Example: 351 foldlAttrs 352 (acc: name: value: { 353 sum = acc.sum + value; 354 names = acc.names ++ [name]; 355 }) 356 { sum = 0; names = []; } 357 { 358 foo = 1; 359 bar = 10; 360 } 361 -> 362 { 363 sum = 11; 364 names = ["bar" "foo"]; 365 } 366 367 foldlAttrs 368 (throw "function not needed") 369 123 370 {}; 371 -> 372 123 373 374 foldlAttrs 375 (acc: _: _: acc) 376 3 377 { z = throw "value not needed"; a = throw "value not needed"; }; 378 -> 379 3 380 381 The accumulator doesn't have to be an attrset. 382 It can be as simple as a number or string. 383 384 foldlAttrs 385 (acc: _: v: acc * 10 + v) 386 1 387 { z = 1; a = 2; }; 388 -> 389 121 390 391 Type: 392 foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a 393 */ 394 foldlAttrs = f: init: set: 395 foldl' 396 (acc: name: f acc name set.${name}) 397 init 398 (attrNames set); 399 400 /* Apply fold functions to values grouped by key. 401 402 Example: 403 foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }] 404 => { a = [ 2 3 ]; } 405 406 Type: 407 foldAttrs :: (Any -> Any -> Any) -> Any -> [AttrSets] -> Any 408 409 */ 410 foldAttrs = 411 # A function, given a value and a collector combines the two. 412 op: 413 # The starting value. 414 nul: 415 # A list of attribute sets to fold together by key. 416 list_of_attrs: 417 foldr (n: a: 418 foldr (name: o: 419 o // { ${name} = op n.${name} (a.${name} or nul); } 420 ) a (attrNames n) 421 ) {} list_of_attrs; 422 423 424 /* Recursively collect sets that verify a given predicate named `pred` 425 from the set `attrs`. The recursion is stopped when the predicate is 426 verified. 427 428 Example: 429 collect isList { a = { b = ["b"]; }; c = [1]; } 430 => [["b"] [1]] 431 432 collect (x: x ? outPath) 433 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } 434 => [{ outPath = "a/"; } { outPath = "b/"; }] 435 436 Type: 437 collect :: (AttrSet -> Bool) -> AttrSet -> [x] 438 */ 439 collect = 440 # Given an attribute's value, determine if recursion should stop. 441 pred: 442 # The attribute set to recursively collect. 443 attrs: 444 if pred attrs then 445 [ attrs ] 446 else if isAttrs attrs then 447 concatMap (collect pred) (attrValues attrs) 448 else 449 []; 450 451 /* Return the cartesian product of attribute set value combinations. 452 453 Example: 454 cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } 455 => [ 456 { a = 1; b = 10; } 457 { a = 1; b = 20; } 458 { a = 2; b = 10; } 459 { a = 2; b = 20; } 460 ] 461 Type: 462 cartesianProductOfSets :: AttrSet -> [AttrSet] 463 */ 464 cartesianProductOfSets = 465 # Attribute set with attributes that are lists of values 466 attrsOfLists: 467 foldl' (listOfAttrs: attrName: 468 concatMap (attrs: 469 map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName} 470 ) listOfAttrs 471 ) [{}] (attrNames attrsOfLists); 472 473 474 /* Utility function that creates a `{name, value}` pair as expected by `builtins.listToAttrs`. 475 476 Example: 477 nameValuePair "some" 6 478 => { name = "some"; value = 6; } 479 480 Type: 481 nameValuePair :: String -> Any -> { name :: String; value :: Any; } 482 */ 483 nameValuePair = 484 # Attribute name 485 name: 486 # Attribute value 487 value: 488 { inherit name value; }; 489 490 491 /* Apply a function to each element in an attribute set, creating a new attribute set. 492 493 Example: 494 mapAttrs (name: value: name + "-" + value) 495 { x = "foo"; y = "bar"; } 496 => { x = "x-foo"; y = "y-bar"; } 497 498 Type: 499 mapAttrs :: (String -> Any -> Any) -> AttrSet -> AttrSet 500 */ 501 mapAttrs = builtins.mapAttrs or 502 (f: set: 503 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))); 504 505 506 /* Like `mapAttrs`, but allows the name of each attribute to be 507 changed in addition to the value. The applied function should 508 return both the new name and value as a `nameValuePair`. 509 510 Example: 511 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) 512 { x = "a"; y = "b"; } 513 => { foo_x = "bar-a"; foo_y = "bar-b"; } 514 515 Type: 516 mapAttrs' :: (String -> Any -> { name :: String; value :: Any; }) -> AttrSet -> AttrSet 517 */ 518 mapAttrs' = 519 # A function, given an attribute's name and value, returns a new `nameValuePair`. 520 f: 521 # Attribute set to map over. 522 set: 523 listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); 524 525 526 /* Call a function for each attribute in the given set and return 527 the result in a list. 528 529 Example: 530 mapAttrsToList (name: value: name + value) 531 { x = "a"; y = "b"; } 532 => [ "xa" "yb" ] 533 534 Type: 535 mapAttrsToList :: (String -> a -> b) -> AttrSet -> [b] 536 537 */ 538 mapAttrsToList = 539 # A function, given an attribute's name and value, returns a new value. 540 f: 541 # Attribute set to map over. 542 attrs: 543 map (name: f name attrs.${name}) (attrNames attrs); 544 545 /* 546 Deconstruct an attrset to a list of name-value pairs as expected by [`builtins.listToAttrs`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-listToAttrs). 547 Each element of the resulting list is an attribute set with these attributes: 548 - `name` (string): The name of the attribute 549 - `value` (any): The value of the attribute 550 551 The following is always true: 552 ```nix 553 builtins.listToAttrs (attrsToList attrs) == attrs 554 ``` 555 556 :::{.warning} 557 The opposite is not always true. In general expect that 558 ```nix 559 attrsToList (builtins.listToAttrs list) != list 560 ``` 561 562 This is because the `listToAttrs` removes duplicate names and doesn't preserve the order of the list. 563 ::: 564 565 Example: 566 attrsToList { foo = 1; bar = "asdf"; } 567 => [ { name = "bar"; value = "asdf"; } { name = "foo"; value = 1; } ] 568 569 Type: 570 attrsToList :: AttrSet -> [ { name :: String; value :: Any; } ] 571 572 */ 573 attrsToList = mapAttrsToList nameValuePair; 574 575 576 /* Like `mapAttrs`, except that it recursively applies itself to 577 the *leaf* attributes of a potentially-nested attribute set: 578 the second argument of the function will never be an attrset. 579 Also, the first argument of the argument function is a *list* 580 of the attribute names that form the path to the leaf attribute. 581 582 For a function that gives you control over what counts as a leaf, 583 see `mapAttrsRecursiveCond`. 584 585 Example: 586 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) 587 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } 588 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } 589 590 Type: 591 mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet 592 */ 593 mapAttrsRecursive = 594 # A function, given a list of attribute names and a value, returns a new value. 595 f: 596 # Set to recursively map over. 597 set: 598 mapAttrsRecursiveCond (as: true) f set; 599 600 601 /* Like `mapAttrsRecursive`, but it takes an additional predicate 602 function that tells it whether to recurse into an attribute 603 set. If it returns false, `mapAttrsRecursiveCond` does not 604 recurse, but does apply the map function. If it returns true, it 605 does recurse, and does not apply the map function. 606 607 Example: 608 # To prevent recursing into derivations (which are attribute 609 # sets with the attribute "type" equal to "derivation"): 610 mapAttrsRecursiveCond 611 (as: !(as ? "type" && as.type == "derivation")) 612 (x: ... do something ...) 613 attrs 614 615 Type: 616 mapAttrsRecursiveCond :: (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet 617 */ 618 mapAttrsRecursiveCond = 619 # A function, given the attribute set the recursion is currently at, determine if to recurse deeper into that attribute set. 620 cond: 621 # A function, given a list of attribute names and a value, returns a new value. 622 f: 623 # Attribute set to recursively map over. 624 set: 625 let 626 recurse = path: 627 let 628 g = 629 name: value: 630 if isAttrs value && cond value 631 then recurse (path ++ [name]) value 632 else f (path ++ [name]) value; 633 in mapAttrs g; 634 in recurse [] set; 635 636 637 /* Generate an attribute set by mapping a function over a list of 638 attribute names. 639 640 Example: 641 genAttrs [ "foo" "bar" ] (name: "x_" + name) 642 => { foo = "x_foo"; bar = "x_bar"; } 643 644 Type: 645 genAttrs :: [ String ] -> (String -> Any) -> AttrSet 646 */ 647 genAttrs = 648 # Names of values in the resulting attribute set. 649 names: 650 # A function, given the name of the attribute, returns the attribute's value. 651 f: 652 listToAttrs (map (n: nameValuePair n (f n)) names); 653 654 655 /* Check whether the argument is a derivation. Any set with 656 `{ type = "derivation"; }` counts as a derivation. 657 658 Example: 659 nixpkgs = import <nixpkgs> {} 660 isDerivation nixpkgs.ruby 661 => true 662 isDerivation "foobar" 663 => false 664 665 Type: 666 isDerivation :: Any -> Bool 667 */ 668 isDerivation = 669 # Value to check. 670 value: value.type or null == "derivation"; 671 672 /* Converts a store path to a fake derivation. 673 674 Type: 675 toDerivation :: Path -> Derivation 676 */ 677 toDerivation = 678 # A store path to convert to a derivation. 679 path: 680 let 681 path' = builtins.storePath path; 682 res = 683 { type = "derivation"; 684 name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path')); 685 outPath = path'; 686 outputs = [ "out" ]; 687 out = res; 688 outputName = "out"; 689 }; 690 in res; 691 692 693 /* If `cond` is true, return the attribute set `as`, 694 otherwise an empty attribute set. 695 696 Example: 697 optionalAttrs (true) { my = "set"; } 698 => { my = "set"; } 699 optionalAttrs (false) { my = "set"; } 700 => { } 701 702 Type: 703 optionalAttrs :: Bool -> AttrSet -> AttrSet 704 */ 705 optionalAttrs = 706 # Condition under which the `as` attribute set is returned. 707 cond: 708 # The attribute set to return if `cond` is `true`. 709 as: 710 if cond then as else {}; 711 712 713 /* Merge sets of attributes and use the function `f` to merge attributes 714 values. 715 716 Example: 717 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] 718 => { a = ["x" "y"]; } 719 720 Type: 721 zipAttrsWithNames :: [ String ] -> (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet 722 */ 723 zipAttrsWithNames = 724 # List of attribute names to zip. 725 names: 726 # A function, accepts an attribute name, all the values, and returns a combined value. 727 f: 728 # List of values from the list of attribute sets. 729 sets: 730 listToAttrs (map (name: { 731 inherit name; 732 value = f name (catAttrs name sets); 733 }) names); 734 735 736 /* Merge sets of attributes and use the function f to merge attribute values. 737 Like `lib.attrsets.zipAttrsWithNames` with all key names are passed for `names`. 738 739 Implementation note: Common names appear multiple times in the list of 740 names, hopefully this does not affect the system because the maximal 741 laziness avoid computing twice the same expression and `listToAttrs` does 742 not care about duplicated attribute names. 743 744 Example: 745 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] 746 => { a = ["x" "y"]; b = ["z"]; } 747 748 Type: 749 zipAttrsWith :: (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet 750 */ 751 zipAttrsWith = 752 builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets); 753 754 755 /* Merge sets of attributes and combine each attribute value in to a list. 756 757 Like `lib.attrsets.zipAttrsWith` with `(name: values: values)` as the function. 758 759 Example: 760 zipAttrs [{a = "x";} {a = "y"; b = "z";}] 761 => { a = ["x" "y"]; b = ["z"]; } 762 763 Type: 764 zipAttrs :: [ AttrSet ] -> AttrSet 765 */ 766 zipAttrs = 767 # List of attribute sets to zip together. 768 sets: 769 zipAttrsWith (name: values: values) sets; 770 771 /* 772 Merge a list of attribute sets together using the `//` operator. 773 In case of duplicate attributes, values from later list elements take precedence over earlier ones. 774 The result is the same as `foldl mergeAttrs { }`, but the performance is better for large inputs. 775 For n list elements, each with an attribute set containing m unique attributes, the complexity of this operation is O(nm log n). 776 777 Type: 778 mergeAttrsList :: [ Attrs ] -> Attrs 779 780 Example: 781 mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ] 782 => { a = 0; b = 1; c = 2; d = 3; } 783 mergeAttrsList [ { a = 0; } { a = 1; } ] 784 => { a = 1; } 785 */ 786 mergeAttrsList = list: 787 let 788 # `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end` 789 # Type: Int -> Int -> Attrs 790 binaryMerge = start: end: 791 # assert start < end; # Invariant 792 if end - start >= 2 then 793 # If there's at least 2 elements, split the range in two, recurse on each part and merge the result 794 # The invariant is satisfied because each half will have at least 1 element 795 binaryMerge start (start + (end - start) / 2) 796 // binaryMerge (start + (end - start) / 2) end 797 else 798 # Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly 799 elemAt list start; 800 in 801 if list == [ ] then 802 # Calling binaryMerge as below would not satisfy its invariant 803 { } 804 else 805 binaryMerge 0 (length list); 806 807 808 /* Does the same as the update operator '//' except that attributes are 809 merged until the given predicate is verified. The predicate should 810 accept 3 arguments which are the path to reach the attribute, a part of 811 the first attribute set and a part of the second attribute set. When 812 the predicate is satisfied, the value of the first attribute set is 813 replaced by the value of the second attribute set. 814 815 Example: 816 recursiveUpdateUntil (path: l: r: path == ["foo"]) { 817 # first attribute set 818 foo.bar = 1; 819 foo.baz = 2; 820 bar = 3; 821 } { 822 #second attribute set 823 foo.bar = 1; 824 foo.quz = 2; 825 baz = 4; 826 } 827 828 => { 829 foo.bar = 1; # 'foo.*' from the second set 830 foo.quz = 2; # 831 bar = 3; # 'bar' from the first set 832 baz = 4; # 'baz' from the second set 833 } 834 835 Type: 836 recursiveUpdateUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet 837 */ 838 recursiveUpdateUntil = 839 # Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments. 840 pred: 841 # Left attribute set of the merge. 842 lhs: 843 # Right attribute set of the merge. 844 rhs: 845 let f = attrPath: 846 zipAttrsWith (n: values: 847 let here = attrPath ++ [n]; in 848 if length values == 1 849 || pred here (elemAt values 1) (head values) then 850 head values 851 else 852 f here values 853 ); 854 in f [] [rhs lhs]; 855 856 857 /* A recursive variant of the update operator //. The recursion 858 stops when one of the attribute values is not an attribute set, 859 in which case the right hand side value takes precedence over the 860 left hand side value. 861 862 Example: 863 recursiveUpdate { 864 boot.loader.grub.enable = true; 865 boot.loader.grub.device = "/dev/hda"; 866 } { 867 boot.loader.grub.device = ""; 868 } 869 870 returns: { 871 boot.loader.grub.enable = true; 872 boot.loader.grub.device = ""; 873 } 874 875 Type: 876 recursiveUpdate :: AttrSet -> AttrSet -> AttrSet 877 */ 878 recursiveUpdate = 879 # Left attribute set of the merge. 880 lhs: 881 # Right attribute set of the merge. 882 rhs: 883 recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs; 884 885 886 /* Returns true if the pattern is contained in the set. False otherwise. 887 888 Example: 889 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } 890 => true 891 892 Type: 893 matchAttrs :: AttrSet -> AttrSet -> Bool 894 */ 895 matchAttrs = 896 # Attribute set structure to match 897 pattern: 898 # Attribute set to find patterns in 899 attrs: 900 assert isAttrs pattern; 901 all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: 902 let pat = head values; val = elemAt values 1; in 903 if length values == 1 then false 904 else if isAttrs pat then isAttrs val && matchAttrs pat val 905 else pat == val 906 ) [pattern attrs])); 907 908 909 /* Override only the attributes that are already present in the old set 910 useful for deep-overriding. 911 912 Example: 913 overrideExisting {} { a = 1; } 914 => {} 915 overrideExisting { b = 2; } { a = 1; } 916 => { b = 2; } 917 overrideExisting { a = 3; b = 2; } { a = 1; } 918 => { a = 1; b = 2; } 919 920 Type: 921 overrideExisting :: AttrSet -> AttrSet -> AttrSet 922 */ 923 overrideExisting = 924 # Original attribute set 925 old: 926 # Attribute set with attributes to override in `old`. 927 new: 928 mapAttrs (name: value: new.${name} or value) old; 929 930 931 /* Turns a list of strings into a human-readable description of those 932 strings represented as an attribute path. The result of this function is 933 not intended to be machine-readable. 934 Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`. 935 936 Example: 937 showAttrPath [ "foo" "10" "bar" ] 938 => "foo.\"10\".bar" 939 showAttrPath [] 940 => "<root attribute path>" 941 942 Type: 943 showAttrPath :: [String] -> String 944 */ 945 showAttrPath = 946 # Attribute path to render to a string 947 path: 948 if path == [] then "<root attribute path>" 949 else concatMapStringsSep "." escapeNixIdentifier path; 950 951 952 /* Get a package output. 953 If no output is found, fallback to `.out` and then to the default. 954 955 Example: 956 getOutput "dev" pkgs.openssl 957 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" 958 959 Type: 960 getOutput :: String -> Derivation -> String 961 */ 962 getOutput = output: pkg: 963 if ! pkg ? outputSpecified || ! pkg.outputSpecified 964 then pkg.${output} or pkg.out or pkg 965 else pkg; 966 967 /* Get a package's `bin` output. 968 If the output does not exist, fallback to `.out` and then to the default. 969 970 Example: 971 getBin pkgs.openssl 972 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r" 973 974 Type: 975 getBin :: Derivation -> String 976 */ 977 getBin = getOutput "bin"; 978 979 980 /* Get a package's `lib` output. 981 If the output does not exist, fallback to `.out` and then to the default. 982 983 Example: 984 getLib pkgs.openssl 985 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-lib" 986 987 Type: 988 getLib :: Derivation -> String 989 */ 990 getLib = getOutput "lib"; 991 992 993 /* Get a package's `dev` output. 994 If the output does not exist, fallback to `.out` and then to the default. 995 996 Example: 997 getDev pkgs.openssl 998 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" 999 1000 Type: 1001 getDev :: Derivation -> String 1002 */ 1003 getDev = getOutput "dev"; 1004 1005 1006 /* Get a package's `man` output. 1007 If the output does not exist, fallback to `.out` and then to the default. 1008 1009 Example: 1010 getMan pkgs.openssl 1011 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-man" 1012 1013 Type: 1014 getMan :: Derivation -> String 1015 */ 1016 getMan = getOutput "man"; 1017 1018 /* Pick the outputs of packages to place in `buildInputs` 1019 1020 Type: chooseDevOutputs :: [Derivation] -> [String] 1021 1022 */ 1023 chooseDevOutputs = 1024 # List of packages to pick `dev` outputs from 1025 drvs: 1026 builtins.map getDev drvs; 1027 1028 /* Make various Nix tools consider the contents of the resulting 1029 attribute set when looking for what to build, find, etc. 1030 1031 This function only affects a single attribute set; it does not 1032 apply itself recursively for nested attribute sets. 1033 1034 Example: 1035 { pkgs ? import <nixpkgs> {} }: 1036 { 1037 myTools = pkgs.lib.recurseIntoAttrs { 1038 inherit (pkgs) hello figlet; 1039 }; 1040 } 1041 1042 Type: 1043 recurseIntoAttrs :: AttrSet -> AttrSet 1044 1045 */ 1046 recurseIntoAttrs = 1047 # An attribute set to scan for derivations. 1048 attrs: 1049 attrs // { recurseForDerivations = true; }; 1050 1051 /* Undo the effect of recurseIntoAttrs. 1052 1053 Type: 1054 dontRecurseIntoAttrs :: AttrSet -> AttrSet 1055 */ 1056 dontRecurseIntoAttrs = 1057 # An attribute set to not scan for derivations. 1058 attrs: 1059 attrs // { recurseForDerivations = false; }; 1060 1061 /* `unionOfDisjoint x y` is equal to `x // y // z` where the 1062 attrnames in `z` are the intersection of the attrnames in `x` and 1063 `y`, and all values `assert` with an error message. This 1064 operator is commutative, unlike (//). 1065 1066 Type: unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet 1067 */ 1068 unionOfDisjoint = x: y: 1069 let 1070 intersection = builtins.intersectAttrs x y; 1071 collisions = lib.concatStringsSep " " (builtins.attrNames intersection); 1072 mask = builtins.mapAttrs (name: value: builtins.throw 1073 "unionOfDisjoint: collision on ${name}; complete list: ${collisions}") 1074 intersection; 1075 in 1076 (x // y) // mask; 1077 1078 # DEPRECATED 1079 zipWithNames = zipAttrsWithNames; 1080 1081 # DEPRECATED 1082 zip = builtins.trace 1083 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; 1084}