2020-04-24 23:36:52 +00:00
{ lib }:
# Operations on attribute sets.
let
inherit ( builtins ) head tail length ;
2022-11-21 17:40:18 +00:00
inherit ( lib . trivial ) flip id mergeAttrs pipe ;
2022-03-30 09:31:56 +00:00
inherit ( lib . strings ) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName ;
inherit ( lib . lists ) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl ;
2020-04-24 23:36:52 +00:00
in
rec {
2023-04-29 16:46:19 +00:00
inherit ( builtins ) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs ;
2020-04-24 23:36:52 +00:00
/* R e t u r n a n a t t r i b u t e f r o m n e s t e d a t t r i b u t e s e t s .
Example :
x = { a = { b = 3 ; } ; }
2022-12-28 21:21:41 +00:00
# ["a" "b"] is equivalent to x.a.b
# 6 is a default value to return if the path does not exist in attrset
2020-04-24 23:36:52 +00:00
attrByPath [ " a " " b " ] 6 x
= > 3
attrByPath [ " z " " z " ] 6 x
= > 6
2022-12-17 10:02:37 +00:00
Type :
attrByPath : : [ String ] -> Any -> AttrSet -> Any
2022-12-28 21:21:41 +00:00
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
attrByPath =
# A list of strings representing the attribute path to return from `set`
attrPath :
# Default value if `attrPath` does not resolve to an existing value
default :
# The nested attribute set to select values from
set :
2020-04-24 23:36:52 +00:00
let attr = head attrPath ;
in
2022-12-17 10:02:37 +00:00
if attrPath == [ ] then set
else if set ? ${ attr }
then attrByPath ( tail attrPath ) default set . ${ attr }
2020-04-24 23:36:52 +00:00
else default ;
/* R e t u r n i f a n a t t r i b u t e f r o m n e s t e d a t t r i b u t e s e t e x i s t s .
Example :
x = { a = { b = 3 ; } ; }
hasAttrByPath [ " a " " b " ] x
= > true
hasAttrByPath [ " z " " z " ] x
= > false
2022-12-17 10:02:37 +00:00
Type :
hasAttrByPath : : [ String ] -> AttrSet -> Bool
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
hasAttrByPath =
# A list of strings representing the attribute path to check from `set`
attrPath :
# The nested attribute set to check
e :
2020-04-24 23:36:52 +00:00
let attr = head attrPath ;
in
if attrPath == [ ] then true
else if e ? ${ attr }
then hasAttrByPath ( tail attrPath ) e . ${ attr }
else false ;
2022-12-17 10:02:37 +00:00
/* C r e a t e a n e w a t t r i b u t e s e t w i t h ` v a l u e ` s e t a t t h e n e s t e d a t t r i b u t e l o c a t i o n s p e c i f i e d i n ` a t t r P a t h ` .
2020-04-24 23:36:52 +00:00
Example :
setAttrByPath [ " a " " b " ] 3
= > { a = { b = 3 ; } ; }
2022-12-17 10:02:37 +00:00
Type :
setAttrByPath : : [ String ] -> Any -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
setAttrByPath =
# A list of strings representing the attribute path to set
attrPath :
# The value to set at the location described by `attrPath`
value :
2021-08-25 08:27:29 +00:00
let
len = length attrPath ;
atDepth = n :
if n == len
then value
else { ${ elemAt attrPath n } = atDepth ( n + 1 ) ; } ;
in atDepth 0 ;
2020-04-24 23:36:52 +00:00
2023-01-11 07:51:40 +00:00
/* L i k e ` a t t r B y P a t h ` , b u t w i t h o u t a d e f a u l t v a l u e . I f i t d o e s n ' t f i n d t h e
2022-12-17 10:02:37 +00:00
path it will throw an error .
2020-04-24 23:36:52 +00:00
Example :
x = { a = { b = 3 ; } ; }
getAttrFromPath [ " a " " b " ] x
= > 3
getAttrFromPath [ " z " " z " ] x
= > error : cannot find attribute ` z . z'
2022-12-17 10:02:37 +00:00
Type :
2022-12-28 21:21:41 +00:00
getAttrFromPath : : [ String ] -> AttrSet -> Any
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
getAttrFromPath =
# A list of strings representing the attribute path to get from `set`
attrPath :
# The nested attribute set to find the value in.
set :
2020-04-24 23:36:52 +00:00
let errorMsg = " c a n n o t f i n d a t t r i b u t e ` " + concatStringsSep " . " attrPath + " ' " ;
2022-12-17 10:02:37 +00:00
in attrByPath attrPath ( abort errorMsg ) set ;
2020-04-24 23:36:52 +00:00
2022-11-21 17:40:18 +00:00
/* M a p e a c h a t t r i b u t e i n t h e g i v e n s e t a n d m e r g e t h e m i n t o a n e w a t t r i b u t e s e t .
Type :
2022-12-28 21:21:41 +00:00
concatMapAttrs : : ( String -> a -> AttrSet ) -> AttrSet -> AttrSet
2022-11-21 17:40:18 +00:00
Example :
concatMapAttrs
( name : value : {
$ { name } = value ;
$ { name + value } = value ;
} )
{ x = " a " ; y = " b " ; }
= > { x = " a " ; xa = " a " ; y = " b " ; yb = " b " ; }
* /
concatMapAttrs = f : flip pipe [ ( mapAttrs f ) attrValues ( foldl' mergeAttrs { } ) ] ;
2020-04-24 23:36:52 +00:00
2022-03-30 09:31:56 +00:00
/* U p d a t e o r s e t s p e c i f i c p a t h s o f a n a t t r i b u t e s e t .
Takes a list of updates to apply and an attribute set to apply them to ,
and returns the attribute set with the updates applied . Updates are
2022-12-17 10:02:37 +00:00
represented as ` { path = . . . ; update = . . . ; } ` values , where ` path ` is a
2022-03-30 09:31:56 +00:00
list of strings representing the attribute path that should be updated ,
and ` update ` is a function that takes the old value at that attribute path
as an argument and returns the new
value it should be .
Properties :
2022-12-17 10:02:37 +00:00
2022-03-30 09:31:56 +00:00
- Updates to deeper attribute paths are applied before updates to more
shallow attribute paths
2022-12-17 10:02:37 +00:00
2022-03-30 09:31:56 +00:00
- Multiple updates to the same attribute path are applied in the order
they appear in the update list
2022-12-17 10:02:37 +00:00
2022-03-30 09:31:56 +00:00
- If any but the last ` path ` element leads into a value that is not an
attribute set , an error is thrown
2022-12-17 10:02:37 +00:00
2022-03-30 09:31:56 +00:00
- If there is an update for an attribute path that doesn't exist ,
accessing the argument in the update function causes an error , but
intermediate attribute sets are implicitly created as needed
Example :
updateManyAttrsByPath [
{
path = [ " a " " b " ] ;
update = old : { d = old . c ; } ;
}
{
path = [ " a " " b " " c " ] ;
update = old : old + 1 ;
}
{
path = [ " x " " y " ] ;
update = old : " x y " ;
}
] { a . b . c = 0 ; }
= > { a = { b = { d = 1 ; } ; } ; x = { y = " x y " ; } ; }
2022-12-17 10:02:37 +00:00
2023-02-02 18:25:31 +00:00
Type : updateManyAttrsByPath : : [ { path : : [ String ] ; update : : ( Any -> Any ) ; } ] -> AttrSet -> AttrSet
2022-03-30 09:31:56 +00:00
* /
updateManyAttrsByPath = let
# When recursing into attributes, instead of updating the `path` of each
# update using `tail`, which needs to allocate an entirely new list,
# we just pass a prefix length to use and make sure to only look at the
# path without the prefix length, so that we can reuse the original list
# entries.
go = prefixLength : hasValue : value : updates :
let
# Splits updates into ones on this level (split.right)
# And ones on levels further down (split.wrong)
split = partition ( el : length el . path == prefixLength ) updates ;
# Groups updates on further down levels into the attributes they modify
nested = groupBy ( el : elemAt el . path prefixLength ) split . wrong ;
# Applies only nested modification to the input value
withNestedMods =
# Return the value directly if we don't have any nested modifications
if split . wrong == [ ] then
if hasValue then value
else
# Throw an error if there is no value. This `head` call here is
# safe, but only in this branch since `go` could only be called
# with `hasValue == false` for nested updates, in which case
# it's also always called with at least one update
let updatePath = ( head split . right ) . path ; in
throw
( " u p d a t e M a n y A t t r s B y P a t h : P a t h ' ${ showAttrPath updatePath } ' d o e s "
+ " n o t e x i s t i n t h e g i v e n v a l u e , b u t t h e f i r s t u p d a t e t o t h i s "
+ " p a t h t r i e s t o a c c e s s t h e e x i s t i n g v a l u e . " )
else
# If there are nested modifications, try to apply them to the value
if ! hasValue then
# But if we don't have a value, just use an empty attribute set
# as the value, but simplify the code a bit
mapAttrs ( name : go ( prefixLength + 1 ) false null ) nested
else if isAttrs value then
# If we do have a value and it's an attribute set, override it
# with the nested modifications
value //
mapAttrs ( name : go ( prefixLength + 1 ) ( value ? ${ name } ) value . ${ name } ) nested
else
# However if it's not an attribute set, we can't apply the nested
# modifications, throw an error
let updatePath = ( head split . wrong ) . path ; in
throw
( " u p d a t e M a n y A t t r s B y P a t h : P a t h ' ${ showAttrPath updatePath } ' n e e d s t o "
+ " b e u p d a t e d , b u t p a t h ' ${ showAttrPath ( take prefixLength updatePath ) } ' "
+ " o f t h e g i v e n v a l u e i s n o t a n a t t r i b u t e s e t , s o w e c a n ' t "
+ " u p d a t e a n a t t r i b u t e i n s i d e o f i t . " ) ;
# We get the final result by applying all the updates on this level
# after having applied all the nested updates
# We use foldl instead of foldl' so that in case of multiple updates,
# intermediate values aren't evaluated if not needed
in foldl ( acc : el : el . update acc ) withNestedMods split . right ;
in updates : value : go 0 true value updates ;
2020-04-24 23:36:52 +00:00
/* R e t u r n t h e s p e c i f i e d a t t r i b u t e s f r o m a s e t .
Example :
attrVals [ " a " " b " " c " ] as
= > [ as . a as . b as . c ]
2022-12-17 10:02:37 +00:00
Type :
attrVals : : [ String ] -> AttrSet -> [ Any ]
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
attrVals =
# The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set
nameList :
# The set to get attribute values from
set : map ( x : set . ${ x } ) nameList ;
2020-04-24 23:36:52 +00:00
/* R e t u r n t h e v a l u e s o f a l l a t t r i b u t e s i n t h e g i v e n s e t , s o r t e d b y
attribute name .
Example :
attrValues { c = 3 ; a = 1 ; b = 2 ; }
= > [ 1 2 3 ]
2022-12-28 21:21:41 +00:00
2022-12-17 10:02:37 +00:00
Type :
attrValues : : AttrSet -> [ Any ]
2020-04-24 23:36:52 +00:00
* /
attrValues = builtins . attrValues or ( attrs : attrVals ( attrNames attrs ) attrs ) ;
/* G i v e n a s e t o f a t t r i b u t e n a m e s , r e t u r n t h e s e t o f t h e c o r r e s p o n d i n g
attributes from the given set .
Example :
getAttrs [ " a " " b " ] { a = 1 ; b = 2 ; c = 3 ; }
= > { a = 1 ; b = 2 ; }
2022-12-17 10:02:37 +00:00
Type :
getAttrs : : [ String ] -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
getAttrs =
# A list of attribute names to get out of `set`
names :
# The set to get the named attributes from
attrs : genAttrs names ( name : attrs . ${ name } ) ;
2020-04-24 23:36:52 +00:00
2023-01-11 07:51:40 +00:00
/* C o l l e c t e a c h a t t r i b u t e n a m e d ` a t t r ` f r o m a l i s t o f a t t r i b u t e
2020-04-24 23:36:52 +00:00
sets . Sets that don't contain the named attribute are ignored .
Example :
catAttrs " a " [ { a = 1 ; } { b = 0 ; } { a = 2 ; } ]
= > [ 1 2 ]
2022-12-17 10:02:37 +00:00
Type :
catAttrs : : String -> [ AttrSet ] -> [ Any ]
2020-04-24 23:36:52 +00:00
* /
catAttrs = builtins . catAttrs or
( attr : l : concatLists ( map ( s : if s ? ${ attr } then [ s . ${ attr } ] else [ ] ) l ) ) ;
/* F i l t e r a n a t t r i b u t e s e t b y r e m o v i n g a l l a t t r i b u t e s f o r w h i c h t h e
given predicate return false .
Example :
filterAttrs ( n : v : n == " f o o " ) { foo = 1 ; bar = 2 ; }
= > { foo = 1 ; }
2022-12-17 10:02:37 +00:00
Type :
filterAttrs : : ( String -> Any -> Bool ) -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
filterAttrs =
# Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute.
pred :
# The attribute set to filter
set :
2020-04-24 23:36:52 +00:00
listToAttrs ( concatMap ( name : let v = set . ${ name } ; in if pred name v then [ ( nameValuePair name v ) ] else [ ] ) ( attrNames set ) ) ;
/* F i l t e r a n a t t r i b u t e s e t r e c u r s i v e l y b y r e m o v i n g a l l a t t r i b u t e s f o r
which the given predicate return false .
Example :
filterAttrsRecursive ( n : v : v != null ) { foo = { bar = null ; } ; }
= > { foo = { } ; }
2022-12-17 10:02:37 +00:00
Type :
filterAttrsRecursive : : ( String -> Any -> Bool ) -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
filterAttrsRecursive =
# Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute.
pred :
# The attribute set to filter
set :
2020-04-24 23:36:52 +00:00
listToAttrs (
concatMap ( name :
let v = set . ${ name } ; in
if pred name v then [
( nameValuePair name (
if isAttrs v then filterAttrsRecursive pred v
else v
) )
] else [ ]
) ( attrNames set )
) ;
2023-03-15 16:39:30 +00:00
/*
Like builtins . foldl' but for attribute sets .
Iterates over every name-value pair in the given attribute set .
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 ` .
Attention :
There is a completely different function
` lib . foldAttrs `
which has nothing to do with this function , despite the similar name .
Example :
foldlAttrs
( acc : name : value : {
sum = acc . sum + value ;
names = acc . names ++ [ name ] ;
} )
{ sum = 0 ; names = [ ] ; }
{
foo = 1 ;
bar = 10 ;
}
->
{
sum = 11 ;
names = [ " b a r " " f o o " ] ;
}
foldlAttrs
( throw " f u n c t i o n n o t n e e d e d " )
123
{ } ;
->
123
foldlAttrs
( _ : _ : v : v )
( throw " i n i t i a l a c c u m u l a t o r n o t n e e d e d " )
{ z = 3 ; a = 2 ; } ;
->
3
The accumulator doesn't have to be an attrset .
It can be as simple as a number or string .
foldlAttrs
( acc : _ : v : acc * 10 + v )
1
{ z = 1 ; a = 2 ; } ;
->
121
Type :
foldlAttrs : : ( a -> String -> b -> a ) -> a -> { . . . : : b } -> a
* /
foldlAttrs = f : init : set :
foldl'
( acc : name : f acc name set . ${ name } )
init
( attrNames set ) ;
2020-04-24 23:36:52 +00:00
/* A p p l y f o l d f u n c t i o n s t o v a l u e s g r o u p e d b y k e y .
Example :
2022-06-16 17:23:12 +00:00
foldAttrs ( item : acc : [ item ] ++ acc ) [ ] [ { a = 2 ; } { a = 3 ; } ]
2020-04-24 23:36:52 +00:00
= > { a = [ 2 3 ] ; }
2022-12-17 10:02:37 +00:00
Type :
foldAttrs : : ( Any -> Any -> Any ) -> Any -> [ AttrSets ] -> Any
2022-12-28 21:21:41 +00:00
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
foldAttrs =
# A function, given a value and a collector combines the two.
op :
# The starting value.
nul :
# A list of attribute sets to fold together by key.
list_of_attrs :
2021-08-05 21:33:18 +00:00
foldr ( n : a :
foldr ( name : o :
2020-04-24 23:36:52 +00:00
o // { ${ name } = op n . ${ name } ( a . ${ name } or nul ) ; }
) a ( attrNames n )
2022-12-17 10:02:37 +00:00
) { } list_of_attrs ;
2020-04-24 23:36:52 +00:00
2023-01-11 07:51:40 +00:00
/* R e c u r s i v e l y c o l l e c t s e t s t h a t v e r i f y a g i v e n p r e d i c a t e n a m e d ` p r e d `
from the set ` attrs ` . The recursion is stopped when the predicate is
2020-04-24 23:36:52 +00:00
verified .
Example :
collect isList { a = { b = [ " b " ] ; } ; c = [ 1 ] ; }
= > [ [ " b " ] [ 1 ] ]
collect ( x : x ? outPath )
{ a = { outPath = " a / " ; } ; b = { outPath = " b / " ; } ; }
= > [ { outPath = " a / " ; } { outPath = " b / " ; } ]
2022-12-17 10:02:37 +00:00
Type :
collect : : ( AttrSet -> Bool ) -> AttrSet -> [ x ]
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
collect =
# Given an attribute's value, determine if recursion should stop.
pred :
# The attribute set to recursively collect.
attrs :
2020-04-24 23:36:52 +00:00
if pred attrs then
[ attrs ]
else if isAttrs attrs then
concatMap ( collect pred ) ( attrValues attrs )
else
[ ] ;
2021-02-05 17:12:51 +00:00
/* R e t u r n t h e c a r t e s i a n p r o d u c t o f a t t r i b u t e s e t v a l u e c o m b i n a t i o n s .
Example :
cartesianProductOfSets { a = [ 1 2 ] ; b = [ 10 20 ] ; }
= > [
{ a = 1 ; b = 10 ; }
{ a = 1 ; b = 20 ; }
{ a = 2 ; b = 10 ; }
{ a = 2 ; b = 20 ; }
]
2022-12-17 10:02:37 +00:00
Type :
2022-12-28 21:21:41 +00:00
cartesianProductOfSets : : AttrSet -> [ AttrSet ]
2021-02-05 17:12:51 +00:00
* /
2022-12-17 10:02:37 +00:00
cartesianProductOfSets =
# Attribute set with attributes that are lists of values
attrsOfLists :
2021-08-25 08:27:29 +00:00
foldl' ( listOfAttrs : attrName :
2021-02-05 17:12:51 +00:00
concatMap ( attrs :
map ( listValue : attrs // { ${ attrName } = listValue ; } ) attrsOfLists . ${ attrName }
) listOfAttrs
) [ { } ] ( attrNames attrsOfLists ) ;
2020-04-24 23:36:52 +00:00
2022-12-17 10:02:37 +00:00
/* U t i l i t y f u n c t i o n t h a t c r e a t e s a ` { n a m e , v a l u e } ` p a i r a s e x p e c t e d b y ` b u i l t i n s . l i s t T o A t t r s ` .
2020-04-24 23:36:52 +00:00
Example :
nameValuePair " s o m e " 6
= > { name = " s o m e " ; value = 6 ; }
2022-12-17 10:02:37 +00:00
Type :
2023-02-02 18:25:31 +00:00
nameValuePair : : String -> Any -> { name : : String ; value : : Any ; }
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
nameValuePair =
# Attribute name
name :
# Attribute value
value :
{ inherit name value ; } ;
2020-04-24 23:36:52 +00:00
2022-12-17 10:02:37 +00:00
/* A p p l y a f u n c t i o n t o e a c h e l e m e n t i n a n a t t r i b u t e s e t , c r e a t i n g a n e w a t t r i b u t e s e t .
2020-04-24 23:36:52 +00:00
Example :
mapAttrs ( name : value : name + " - " + value )
{ x = " f o o " ; y = " b a r " ; }
= > { x = " x - f o o " ; y = " y - b a r " ; }
2022-12-17 10:02:37 +00:00
Type :
mapAttrs : : ( String -> Any -> Any ) -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
mapAttrs = builtins . mapAttrs or
( f : set :
listToAttrs ( map ( attr : { name = attr ; value = f attr set . ${ attr } ; } ) ( attrNames set ) ) ) ;
2023-01-11 07:51:40 +00:00
/* L i k e ` m a p A t t r s ` , b u t a l l o w s t h e n a m e o f e a c h a t t r i b u t e t o b e
2020-04-24 23:36:52 +00:00
changed in addition to the value . The applied function should
2023-01-11 07:51:40 +00:00
return both the new name and value as a ` nameValuePair ` .
2020-04-24 23:36:52 +00:00
Example :
mapAttrs' ( name : value : nameValuePair ( " f o o _ " + name ) ( " b a r - " + value ) )
{ x = " a " ; y = " b " ; }
= > { foo_x = " b a r - a " ; foo_y = " b a r - b " ; }
2022-12-17 10:02:37 +00:00
Type :
2023-02-02 18:25:31 +00:00
mapAttrs' : : ( String -> Any -> { name : : String ; value : : Any ; } ) -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
mapAttrs' =
# A function, given an attribute's name and value, returns a new `nameValuePair`.
f :
# Attribute set to map over.
set :
2020-04-24 23:36:52 +00:00
listToAttrs ( map ( attr : f attr set . ${ attr } ) ( attrNames set ) ) ;
/* C a l l a f u n c t i o n f o r e a c h a t t r i b u t e i n t h e g i v e n s e t a n d r e t u r n
the result in a list .
Example :
mapAttrsToList ( name : value : name + value )
{ x = " a " ; y = " b " ; }
= > [ " x a " " y b " ]
2022-12-17 10:02:37 +00:00
Type :
mapAttrsToList : : ( String -> a -> b ) -> AttrSet -> [ b ]
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
mapAttrsToList =
# A function, given an attribute's name and value, returns a new value.
f :
# Attribute set to map over.
attrs :
2020-04-24 23:36:52 +00:00
map ( name : f name attrs . ${ name } ) ( attrNames attrs ) ;
2023-01-11 07:51:40 +00:00
/* L i k e ` m a p A t t r s ` , e x c e p t t h a t i t r e c u r s i v e l y a p p l i e s i t s e l f t o
2023-02-02 18:25:31 +00:00
the * leaf * attributes of a potentially-nested attribute set :
the second argument of the function will never be an attrset .
Also , the first argument of the argument function is a * list *
of the attribute names that form the path to the leaf attribute .
For a function that gives you control over what counts as a leaf ,
see ` mapAttrsRecursiveCond ` .
2020-04-24 23:36:52 +00:00
Example :
mapAttrsRecursive ( path : value : concatStringsSep " - " ( path ++ [ value ] ) )
{ n = { a = " A " ; m = { b = " B " ; c = " C " ; } ; } ; d = " D " ; }
= > { n = { a = " n - a - A " ; m = { b = " n - m - b - B " ; c = " n - m - c - C " ; } ; } ; d = " d - D " ; }
2022-12-17 10:02:37 +00:00
Type :
mapAttrsRecursive : : ( [ String ] -> a -> b ) -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
mapAttrsRecursive =
# A function, given a list of attribute names and a value, returns a new value.
f :
# Set to recursively map over.
set :
mapAttrsRecursiveCond ( as : true ) f set ;
2020-04-24 23:36:52 +00:00
2023-01-11 07:51:40 +00:00
/* L i k e ` m a p A t t r s R e c u r s i v e ` , b u t i t t a k e s a n a d d i t i o n a l p r e d i c a t e
2022-02-10 20:34:41 +00:00
function that tells it whether to recurse into an attribute
2023-01-11 07:51:40 +00:00
set . If it returns false , ` mapAttrsRecursiveCond ` does not
2020-06-18 07:06:33 +00:00
recurse , but does apply the map function . If it returns true , it
2020-04-24 23:36:52 +00:00
does recurse , and does not apply the map function .
Example :
# To prevent recursing into derivations (which are attribute
# sets with the attribute "type" equal to "derivation"):
mapAttrsRecursiveCond
( as : ! ( as ? " t y p e " && as . type == " d e r i v a t i o n " ) )
( x : . . . do something . . . )
attrs
2022-12-17 10:02:37 +00:00
Type :
mapAttrsRecursiveCond : : ( AttrSet -> Bool ) -> ( [ String ] -> a -> b ) -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
mapAttrsRecursiveCond =
# A function, given the attribute set the recursion is currently at, determine if to recurse deeper into that attribute set.
cond :
# A function, given a list of attribute names and a value, returns a new value.
f :
# Attribute set to recursively map over.
set :
2020-04-24 23:36:52 +00:00
let
2022-01-13 20:06:32 +00:00
recurse = path :
2020-04-24 23:36:52 +00:00
let
g =
name : value :
if isAttrs value && cond value
then recurse ( path ++ [ name ] ) value
else f ( path ++ [ name ] ) value ;
2022-01-13 20:06:32 +00:00
in mapAttrs g ;
2020-04-24 23:36:52 +00:00
in recurse [ ] set ;
/* G e n e r a t e a n a t t r i b u t e s e t b y m a p p i n g a f u n c t i o n o v e r a l i s t o f
attribute names .
Example :
genAttrs [ " f o o " " b a r " ] ( name : " x _ " + name )
= > { foo = " x _ f o o " ; bar = " x _ b a r " ; }
2022-12-17 10:02:37 +00:00
Type :
genAttrs : : [ String ] -> ( String -> Any ) -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
genAttrs =
# Names of values in the resulting attribute set.
names :
# A function, given the name of the attribute, returns the attribute's value.
f :
2020-04-24 23:36:52 +00:00
listToAttrs ( map ( n : nameValuePair n ( f n ) ) names ) ;
/* C h e c k w h e t h e r t h e a r g u m e n t i s a d e r i v a t i o n . A n y s e t w i t h
2022-12-17 10:02:37 +00:00
` { type = " d e r i v a t i o n " ; } ` counts as a derivation .
2020-04-24 23:36:52 +00:00
Example :
nixpkgs = import <nixpkgs> { }
isDerivation nixpkgs . ruby
= > true
isDerivation " f o o b a r "
= > false
2022-12-17 10:02:37 +00:00
Type :
isDerivation : : Any -> Bool
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
isDerivation =
# Value to check.
value : value . type or null == " d e r i v a t i o n " ;
2020-04-24 23:36:52 +00:00
2022-12-17 10:02:37 +00:00
/* C o n v e r t s a s t o r e p a t h t o a f a k e d e r i v a t i o n .
Type :
toDerivation : : Path -> Derivation
* /
toDerivation =
# A store path to convert to a derivation.
path :
let
path' = builtins . storePath path ;
res =
{ type = " d e r i v a t i o n " ;
name = sanitizeDerivationName ( builtins . substring 33 ( -1 ) ( baseNameOf path' ) ) ;
outPath = path' ;
outputs = [ " o u t " ] ;
out = res ;
outputName = " o u t " ;
} ;
2020-04-24 23:36:52 +00:00
in res ;
2022-12-17 10:02:37 +00:00
/* I f ` c o n d ` i s t r u e , r e t u r n t h e a t t r i b u t e s e t ` a s ` ,
2020-04-24 23:36:52 +00:00
otherwise an empty attribute set .
Example :
optionalAttrs ( true ) { my = " s e t " ; }
= > { my = " s e t " ; }
optionalAttrs ( false ) { my = " s e t " ; }
= > { }
2022-12-17 10:02:37 +00:00
Type :
2022-12-28 21:21:41 +00:00
optionalAttrs : : Bool -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
optionalAttrs =
# Condition under which the `as` attribute set is returned.
cond :
# The attribute set to return if `cond` is `true`.
as :
if cond then as else { } ;
2020-04-24 23:36:52 +00:00
2022-12-17 10:02:37 +00:00
/* M e r g e s e t s o f a t t r i b u t e s a n d u s e t h e f u n c t i o n ` f ` t o m e r g e a t t r i b u t e s
2020-04-24 23:36:52 +00:00
values .
Example :
zipAttrsWithNames [ " a " ] ( name : vs : vs ) [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
= > { a = [ " x " " y " ] ; }
2022-12-17 10:02:37 +00:00
Type :
zipAttrsWithNames : : [ String ] -> ( String -> [ Any ] -> Any ) -> [ AttrSet ] -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
zipAttrsWithNames =
# List of attribute names to zip.
names :
# A function, accepts an attribute name, all the values, and returns a combined value.
f :
# List of values from the list of attribute sets.
sets :
2020-04-24 23:36:52 +00:00
listToAttrs ( map ( name : {
inherit name ;
value = f name ( catAttrs name sets ) ;
} ) names ) ;
2022-12-17 10:02:37 +00:00
/* M e r g e s e t s o f a t t r i b u t e s a n d u s e t h e f u n c t i o n f t o m e r g e a t t r i b u t e v a l u e s .
Like ` lib . attrsets . zipAttrsWithNames ` with all key names are passed for ` names ` .
Implementation note : Common names appear multiple times in the list of
2020-04-24 23:36:52 +00:00
names , hopefully this does not affect the system because the maximal
2022-12-17 10:02:37 +00:00
laziness avoid computing twice the same expression and ` listToAttrs ` does
2020-04-24 23:36:52 +00:00
not care about duplicated attribute names .
Example :
zipAttrsWith ( name : values : values ) [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
2023-02-02 18:25:31 +00:00
= > { a = [ " x " " y " ] ; b = [ " z " ] ; }
2022-12-17 10:02:37 +00:00
Type :
2022-12-28 21:21:41 +00:00
zipAttrsWith : : ( String -> [ Any ] -> Any ) -> [ AttrSet ] -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-01-13 20:06:32 +00:00
zipAttrsWith =
builtins . zipAttrsWith or ( f : sets : zipAttrsWithNames ( concatMap attrNames sets ) f sets ) ;
2020-04-24 23:36:52 +00:00
2022-12-17 10:02:37 +00:00
/* M e r g e s e t s o f a t t r i b u t e s a n d c o m b i n e e a c h a t t r i b u t e v a l u e i n t o a l i s t .
2023-01-11 07:51:40 +00:00
Like ` lib . attrsets . zipAttrsWith ` with ` ( name : values : values ) ` as the function .
2022-12-17 10:02:37 +00:00
Example :
zipAttrs [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
2023-02-02 18:25:31 +00:00
= > { a = [ " x " " y " ] ; b = [ " z " ] ; }
2022-12-17 10:02:37 +00:00
Type :
zipAttrs : : [ AttrSet ] -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
zipAttrs =
# List of attribute sets to zip together.
sets :
zipAttrsWith ( name : values : values ) sets ;
2020-04-24 23:36:52 +00:00
/* D o e s t h e s a m e a s t h e u p d a t e o p e r a t o r ' / / ' e x c e p t t h a t a t t r i b u t e s a r e
merged until the given predicate is verified . The predicate should
accept 3 arguments which are the path to reach the attribute , a part of
the first attribute set and a part of the second attribute set . When
2022-12-17 10:02:37 +00:00
the predicate is satisfied , the value of the first attribute set is
2020-04-24 23:36:52 +00:00
replaced by the value of the second attribute set .
Example :
recursiveUpdateUntil ( path : l : r : path == [ " f o o " ] ) {
# first attribute set
foo . bar = 1 ;
foo . baz = 2 ;
bar = 3 ;
} {
#second attribute set
foo . bar = 1 ;
foo . quz = 2 ;
baz = 4 ;
}
2022-12-17 10:02:37 +00:00
= > {
2020-04-24 23:36:52 +00:00
foo . bar = 1 ; # 'foo.*' from the second set
foo . quz = 2 ; #
bar = 3 ; # 'bar' from the first set
baz = 4 ; # 'baz' from the second set
}
2022-12-17 10:02:37 +00:00
Type :
recursiveUpdateUntil : : ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet
* /
recursiveUpdateUntil =
# 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.
pred :
# Left attribute set of the merge.
lhs :
# Right attribute set of the merge.
rhs :
2020-04-24 23:36:52 +00:00
let f = attrPath :
zipAttrsWith ( n : values :
let here = attrPath ++ [ n ] ; in
2022-01-13 20:06:32 +00:00
if length values == 1
|| pred here ( elemAt values 1 ) ( head values ) then
2020-04-24 23:36:52 +00:00
head values
else
f here values
) ;
in f [ ] [ rhs lhs ] ;
2022-12-17 10:02:37 +00:00
2020-04-24 23:36:52 +00:00
/* A r e c u r s i v e v a r i a n t o f t h e u p d a t e o p e r a t o r ‘ / / ’ . T h e r e c u r s i o n
stops when one of the attribute values is not an attribute set ,
in which case the right hand side value takes precedence over the
left hand side value .
Example :
recursiveUpdate {
boot . loader . grub . enable = true ;
boot . loader . grub . device = " / d e v / h d a " ;
} {
boot . loader . grub . device = " " ;
}
returns : {
boot . loader . grub . enable = true ;
boot . loader . grub . device = " " ;
}
2022-12-17 10:02:37 +00:00
Type :
2022-12-28 21:21:41 +00:00
recursiveUpdate : : AttrSet -> AttrSet -> AttrSet
2022-12-17 10:02:37 +00:00
* /
recursiveUpdate =
# Left attribute set of the merge.
lhs :
# Right attribute set of the merge.
rhs :
recursiveUpdateUntil ( path : lhs : rhs : ! ( isAttrs lhs && isAttrs rhs ) ) lhs rhs ;
2020-04-24 23:36:52 +00:00
/* R e t u r n s t r u e i f t h e p a t t e r n i s c o n t a i n e d i n t h e s e t . F a l s e o t h e r w i s e .
Example :
matchAttrs { cpu = { } ; } { cpu = { bits = 64 ; } ; }
= > true
2022-12-17 10:02:37 +00:00
Type :
matchAttrs : : AttrSet -> AttrSet -> Bool
* /
matchAttrs =
2022-12-28 21:21:41 +00:00
# Attribute set structure to match
2022-12-17 10:02:37 +00:00
pattern :
# Attribute set to find patterns in
attrs :
assert isAttrs pattern ;
2022-01-13 20:06:32 +00:00
all id ( attrValues ( zipAttrsWithNames ( attrNames pattern ) ( n : values :
let pat = head values ; val = elemAt values 1 ; in
2020-04-24 23:36:52 +00:00
if length values == 1 then false
else if isAttrs pat then isAttrs val && matchAttrs pat val
else pat == val
) [ pattern attrs ] ) ) ;
2022-12-17 10:02:37 +00:00
2020-04-24 23:36:52 +00:00
/* O v e r r i d e o n l y t h e a t t r i b u t e s t h a t a r e a l r e a d y p r e s e n t i n t h e o l d s e t
useful for deep-overriding .
Example :
overrideExisting { } { a = 1 ; }
= > { }
overrideExisting { b = 2 ; } { a = 1 ; }
= > { b = 2 ; }
overrideExisting { a = 3 ; b = 2 ; } { a = 1 ; }
= > { a = 1 ; b = 2 ; }
2022-12-17 10:02:37 +00:00
Type :
overrideExisting : : AttrSet -> AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
2022-12-17 10:02:37 +00:00
overrideExisting =
# Original attribute set
old :
# Attribute set with attributes to override in `old`.
new :
2020-04-24 23:36:52 +00:00
mapAttrs ( name : value : new . ${ name } or value ) old ;
2022-12-17 10:02:37 +00:00
2022-03-30 09:31:56 +00:00
/* T u r n s a l i s t o f s t r i n g s i n t o a h u m a n - r e a d a b l e d e s c r i p t i o n o f t h o s e
strings represented as an attribute path . The result of this function is
not intended to be machine-readable .
2022-12-28 21:21:41 +00:00
Create a new attribute set with ` value ` set at the nested attribute location specified in ` attrPath ` .
2022-03-30 09:31:56 +00:00
Example :
showAttrPath [ " f o o " " 1 0 " " b a r " ]
= > " f o o . \" 1 0 \" . b a r "
showAttrPath [ ]
= > " < r o o t a t t r i b u t e p a t h > "
2022-12-17 10:02:37 +00:00
Type :
showAttrPath : : [ String ] -> String
2022-03-30 09:31:56 +00:00
* /
2022-12-17 10:02:37 +00:00
showAttrPath =
# Attribute path to render to a string
path :
2022-03-30 09:31:56 +00:00
if path == [ ] then " < r o o t a t t r i b u t e p a t h > "
else concatMapStringsSep " . " escapeNixIdentifier path ;
2022-12-17 10:02:37 +00:00
2020-04-24 23:36:52 +00:00
/* G e t a p a c k a g e o u t p u t .
If no output is found , fallback to ` . out ` and then to the default .
Example :
getOutput " d e v " pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - d e v "
2022-12-17 10:02:37 +00:00
Type :
getOutput : : String -> Derivation -> String
2020-04-24 23:36:52 +00:00
* /
getOutput = output : pkg :
2021-10-28 06:52:43 +00:00
if ! pkg ? outputSpecified || ! pkg . outputSpecified
2020-04-24 23:36:52 +00:00
then pkg . ${ output } or pkg . out or pkg
else pkg ;
2022-12-17 10:02:37 +00:00
/* G e t a p a c k a g e ' s ` b i n ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-28 21:21:41 +00:00
getBin pkgs . openssl
2022-12-17 10:02:37 +00:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r "
Type :
2022-12-28 21:21:41 +00:00
getBin : : Derivation -> String
2022-12-17 10:02:37 +00:00
* /
2020-04-24 23:36:52 +00:00
getBin = getOutput " b i n " ;
2022-12-17 10:02:37 +00:00
/* G e t a p a c k a g e ' s ` l i b ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-28 21:21:41 +00:00
getLib pkgs . openssl
2022-12-17 10:02:37 +00:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - l i b "
Type :
2022-12-28 21:21:41 +00:00
getLib : : Derivation -> String
2022-12-17 10:02:37 +00:00
* /
2020-04-24 23:36:52 +00:00
getLib = getOutput " l i b " ;
2022-12-17 10:02:37 +00:00
/* G e t a p a c k a g e ' s ` d e v ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-28 21:21:41 +00:00
getDev pkgs . openssl
2022-12-17 10:02:37 +00:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - d e v "
Type :
2022-12-28 21:21:41 +00:00
getDev : : Derivation -> String
2022-12-17 10:02:37 +00:00
* /
2020-04-24 23:36:52 +00:00
getDev = getOutput " d e v " ;
2022-12-17 10:02:37 +00:00
/* G e t a p a c k a g e ' s ` m a n ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-28 21:21:41 +00:00
getMan pkgs . openssl
2022-12-17 10:02:37 +00:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - m a n "
Type :
2022-12-28 21:21:41 +00:00
getMan : : Derivation -> String
2022-12-17 10:02:37 +00:00
* /
2020-07-18 16:06:22 +00:00
getMan = getOutput " m a n " ;
2020-04-24 23:36:52 +00:00
2022-12-28 21:21:41 +00:00
/* P i c k t h e o u t p u t s o f p a c k a g e s t o p l a c e i n ` b u i l d I n p u t s `
Type : chooseDevOutputs : : [ Derivation ] -> [ String ]
* /
2022-12-17 10:02:37 +00:00
chooseDevOutputs =
# List of packages to pick `dev` outputs from
drvs :
builtins . map getDev drvs ;
2020-04-24 23:36:52 +00:00
/* M a k e v a r i o u s N i x t o o l s c o n s i d e r t h e c o n t e n t s o f t h e r e s u l t i n g
attribute set when looking for what to build , find , etc .
This function only affects a single attribute set ; it does not
apply itself recursively for nested attribute sets .
2022-12-17 10:02:37 +00:00
Example :
{ pkgs ? import <nixpkgs> { } }:
{
myTools = pkgs . lib . recurseIntoAttrs {
inherit ( pkgs ) hello figlet ;
} ;
}
Type :
recurseIntoAttrs : : AttrSet -> AttrSet
2022-12-28 21:21:41 +00:00
2020-04-24 23:36:52 +00:00
* /
recurseIntoAttrs =
2022-12-17 10:02:37 +00:00
# An attribute set to scan for derivations.
attrs :
attrs // { recurseForDerivations = true ; } ;
2020-04-24 23:36:52 +00:00
/* U n d o t h e e f f e c t o f r e c u r s e I n t o A t t r s .
2022-12-17 10:02:37 +00:00
Type :
2022-12-28 21:21:41 +00:00
dontRecurseIntoAttrs : : AttrSet -> AttrSet
2020-04-24 23:36:52 +00:00
* /
dontRecurseIntoAttrs =
2022-12-17 10:02:37 +00:00
# An attribute set to not scan for derivations.
attrs :
attrs // { recurseForDerivations = false ; } ;
2020-04-24 23:36:52 +00:00
2022-10-21 18:38:19 +00:00
/* ` u n i o n O f D i s j o i n t x y ` i s e q u a l t o ` x / / y / / z ` w h e r e t h e
attrnames in ` z ` are the intersection of the attrnames in ` x ` and
` y ` , and all values ` assert ` with an error message . This
2022-12-28 21:21:41 +00:00
operator is commutative , unlike ( // ) .
Type : unionOfDisjoint : : AttrSet -> AttrSet -> AttrSet
* /
2022-10-21 18:38:19 +00:00
unionOfDisjoint = x : y :
let
intersection = builtins . intersectAttrs x y ;
collisions = lib . concatStringsSep " " ( builtins . attrNames intersection ) ;
mask = builtins . mapAttrs ( name : value : builtins . throw
" u n i o n O f D i s j o i n t : c o l l i s i o n o n ${ name } ; c o m p l e t e l i s t : ${ collisions } " )
intersection ;
in
( x // y ) // mask ;
2022-12-28 21:21:41 +00:00
# DEPRECATED
2020-04-24 23:36:52 +00:00
zipWithNames = zipAttrsWithNames ;
2022-12-28 21:21:41 +00:00
# DEPRECATED
2020-04-24 23:36:52 +00:00
zip = builtins . trace
" l i b . z i p i s d e p r e c a t e d , u s e l i b . z i p A t t r s W i t h i n s t e a d " zipAttrsWith ;
}