at 15.09-beta 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 /* 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}