Merge pull request #13546 from zimbatm/document-stdlib

Document stdlib
This commit is contained in:
Domen Kožar 2016-03-10 12:31:33 +00:00
commit 721462634f
3 changed files with 600 additions and 145 deletions

View file

@ -12,9 +12,15 @@ rec {
inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
/* Return an attribute from nested attribute sets. For instance
["x" "y"] applied to some set e returns e.x.y, if it exists. The
default value is returned otherwise. */
/* Return an attribute from nested attribute sets.
Example:
x = { a = { b = 3; }; }
attrByPath ["a" "b"] 6 x
=> 3
attrByPath ["z" "z"] 6 x
=> 6
*/
attrByPath = attrPath: default: e:
let attr = head attrPath;
in
@ -24,8 +30,15 @@ rec {
else default;
/* Return if an attribute from nested attribute set exists.
For instance ["x" "y"] applied to some set e returns true, if e.x.y exists. False
is returned otherwise. */
Example:
x = { a = { b = 3; }; }
hasAttrByPath ["a" "b"] x
=> true
hasAttrByPath ["z" "z"] x
=> false
*/
hasAttrByPath = attrPath: e:
let attr = head attrPath;
in
@ -35,14 +48,28 @@ rec {
else false;
/* Return nested attribute set in which an attribute is set. For instance
["x" "y"] applied with some value v returns `x.y = v;' */
/* Return nested attribute set in which an attribute is set.
Example:
setAttrByPath ["a" "b"] 3
=> { a = { b = 3; }; }
*/
setAttrByPath = attrPath: value:
if attrPath == [] then value
else listToAttrs
[ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ];
/* Like `getAttrPath' without a default value. If it doesn't find the
path it will throw.
Example:
x = { a = { b = 3; }; }
getAttrFromPath ["a" "b"] x
=> 3
getAttrFromPath ["z" "z"] x
=> error: cannot find attribute `z.z'
*/
getAttrFromPath = attrPath: set:
let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
in attrByPath attrPath (abort errorMsg) set;
@ -109,9 +136,11 @@ rec {
) (attrNames set)
);
/* foldAttrs: apply fold functions to values grouped by key. Eg accumulate values as list:
foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
=> { a = [ 2 3 ]; }
/* Apply fold functions to values grouped by key.
Example:
foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
=> { a = [ 2 3 ]; }
*/
foldAttrs = op: nul: list_of_attrs:
fold (n: a:
@ -147,7 +176,12 @@ rec {
/* Utility function that creates a {name, value} pair as expected by
builtins.listToAttrs. */
builtins.listToAttrs.
Example:
nameValuePair "some" 6
=> { name = "some"; value = 6; }
*/
nameValuePair = name: value: { inherit name value; };
@ -248,11 +282,19 @@ rec {
listToAttrs (map (n: nameValuePair n (f n)) names);
/* Check whether the argument is a derivation. */
/* Check whether the argument is a derivation. Any set with
{ type = "derivation"; } counts as a derivation.
Example:
nixpkgs = import <nixpkgs> {}
isDerivation nixpkgs.ruby
=> true
isDerivation "foobar"
=> false
*/
isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
/* Convert a store path to a fake derivation. */
/* Converts a store path to a fake derivation. */
toDerivation = path:
let path' = builtins.storePath path; in
{ type = "derivation";
@ -262,32 +304,49 @@ rec {
};
/* If the Boolean `cond' is true, return the attribute set `as',
otherwise an empty attribute set. */
/* If `cond' is true, return the attribute set `as',
otherwise an empty attribute set.
Example:
optionalAttrs (true) { my = "set"; }
=> { my = "set"; }
optionalAttrs (false) { my = "set"; }
=> { }
*/
optionalAttrs = cond: as: if cond then as else {};
/* Merge sets of attributes and use the function f to merge attributes
values. */
values.
Example:
zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
=> { a = ["x" "y"]; }
*/
zipAttrsWithNames = names: f: sets:
listToAttrs (map (name: {
inherit name;
value = f name (catAttrs name sets);
}) names);
# implentation note: Common names appear multiple times in the list of
# names, hopefully this does not affect the system because the maximal
# laziness avoid computing twice the same expression and listToAttrs does
# not care about duplicated attribute names.
/* Implentation note: Common names appear multiple times in the list of
names, hopefully this does not affect the system because the maximal
laziness avoid computing twice the same expression and listToAttrs does
not care about duplicated attribute names.
Example:
zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
=> { a = ["x" "y"]; b = ["z"] }
*/
zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
/* Like `zipAttrsWith' with `(name: values: value)' as the function.
Example:
zipAttrs [{a = "x";} {a = "y"; b = "z";}]
=> { a = ["x" "y"]; b = ["z"] }
*/
zipAttrs = zipAttrsWith (name: values: values);
/* backward compatibility */
zipWithNames = zipAttrsWithNames;
zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
/* Does the same as the update operator '//' except that attributes are
merged until the given pedicate is verified. The predicate should
accept 3 arguments which are the path to reach the attribute, a part of
@ -351,6 +410,15 @@ rec {
!(isAttrs lhs && isAttrs rhs)
) lhs rhs;
/* Returns true if the pattern is contained in the set. False otherwise.
FIXME(zimbatm): this example doesn't work !!!
Example:
sys = mkSystem { }
matchAttrs { cpu = { bits = 64; }; } sys
=> true
*/
matchAttrs = pattern: attrs:
fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
let pat = head values; val = head (tail values); in
@ -359,10 +427,23 @@ rec {
else pat == val
) [pattern attrs]));
# override only the attributes that are already present in the old set
# useful for deep-overriding
/* Override only the attributes that are already present in the old set
useful for deep-overriding.
Example:
x = { a = { b = 4; c = 3; }; }
overrideExisting x { a = { b = 6; d = 2; }; }
=> { a = { b = 6; d = 2; }; }
*/
overrideExisting = old: new:
old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old));
deepSeqAttrs = x: y: deepSeqList (attrValues x) y;
/*** deprecated stuff ***/
deepSeqAttrs = throw "removed 2016-02-29 because unused and broken";
zipWithNames = zipAttrsWithNames;
zip = builtins.trace
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
}

View file

@ -6,17 +6,26 @@ rec {
inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
/* Create a list consisting of a single element. `singleton x' is
sometimes more convenient with respect to indentation than `[x]'
when x spans multiple lines.
# Create a list consisting of a single element. `singleton x' is
# sometimes more convenient with respect to indentation than `[x]'
# when x spans multiple lines.
Example:
singleton "foo"
=> [ "foo" ]
*/
singleton = x: [x];
/* "Fold" a binary function `op' between successive elements of
`list' with `nul' as the starting value, i.e., `fold op nul [x_1
x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
Haskell's foldr).
# "Fold" a binary function `op' between successive elements of
# `list' with `nul' as the starting value, i.e., `fold op nul [x_1
# x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
# Haskell's foldr).
Example:
concat = fold (a: b: a + b) "z"
concat [ "a" "b" "c" ]
=> "abcnul"
*/
fold = op: nul: list:
let
len = length list;
@ -26,8 +35,14 @@ rec {
else op (elemAt list n) (fold' (n + 1));
in fold' 0;
# Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
# x_1) x_2) ... x_n)'.
/* Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
x_1) x_2) ... x_n)'.
Example:
lconcat = foldl (a: b: a + b) "z"
lconcat [ "a" "b" "c" ]
=> "zabc"
*/
foldl = op: nul: list:
let
len = length list;
@ -37,13 +52,22 @@ rec {
else op (foldl' (n - 1)) (elemAt list n);
in foldl' (length list - 1);
/* Strict version of foldl.
# Strict version of foldl.
The difference is that evaluation is forced upon access. Usually used
with small whole results (in contract with lazily-generated list or large
lists where only a part is consumed.)
*/
foldl' = builtins.foldl' or foldl;
/* Map with index
# Map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
# ["a-1" "b-2"]'. FIXME: why does this start to count at 1?
FIXME(zimbatm): why does this start to count at 1?
Example:
imap (i: v: "${v}-${toString i}") ["a" "b"]
=> [ "a-1" "b-2" ]
*/
imap =
if builtins ? genList then
f: list: genList (n: f (n + 1) (elemAt list n)) (length list)
@ -57,73 +81,141 @@ rec {
else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1);
in imap' 0;
/* Map and concatenate the result.
# Map and concatenate the result.
Example:
concatMap (x: [x] ++ ["z"]) ["a" "b"]
=> [ "a" "z" "b" "z" ]
*/
concatMap = f: list: concatLists (map f list);
/* Flatten the argument into a single list; that is, nested lists are
spliced into the top-level lists.
# Flatten the argument into a single list; that is, nested lists are
# spliced into the top-level lists. E.g., `flatten [1 [2 [3] 4] 5]
# == [1 2 3 4 5]' and `flatten 1 == [1]'.
Example:
flatten [1 [2 [3] 4] 5]
=> [1 2 3 4 5]
flatten 1
=> [1]
*/
flatten = x:
if isList x
then foldl' (x: y: x ++ (flatten y)) [] x
else [x];
/* Remove elements equal to 'e' from a list. Useful for buildInputs.
# Remove elements equal to 'e' from a list. Useful for buildInputs.
Example:
remove 3 [ 1 3 4 3 ]
=> [ 1 4 ]
*/
remove = e: filter (x: x != e);
/* Find the sole element in the list matching the specified
predicate, returns `default' if no such element exists, or
`multiple' if there are multiple matching elements.
# Find the sole element in the list matching the specified
# predicate, returns `default' if no such element exists, or
# `multiple' if there are multiple matching elements.
Example:
findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
=> "multiple"
findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
=> 3
findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
=> "none"
*/
findSingle = pred: default: multiple: list:
let found = filter pred list; len = length found;
in if len == 0 then default
else if len != 1 then multiple
else head found;
/* Find the first element in the list matching the specified
predicate or returns `default' if no such element exists.
# Find the first element in the list matching the specified
# predicate or returns `default' if no such element exists.
Example:
findFirst (x: x > 3) 7 [ 1 6 4 ]
=> 6
findFirst (x: x > 9) 7 [ 1 6 4 ]
=> 7
*/
findFirst = pred: default: list:
let found = filter pred list;
in if found == [] then default else head found;
/* Return true iff function `pred' returns true for at least element
of `list'.
# Return true iff function `pred' returns true for at least element
# of `list'.
Example:
any isString [ 1 "a" { } ]
=> true
any isString [ 1 { } ]
=> false
*/
any = builtins.any or (pred: fold (x: y: if pred x then true else y) false);
/* Return true iff function `pred' returns true for all elements of
`list'.
# Return true iff function `pred' returns true for all elements of
# `list'.
Example:
all (x: x < 3) [ 1 2 ]
=> true
all (x: x < 3) [ 1 2 3 ]
=> false
*/
all = builtins.all or (pred: fold (x: y: if pred x then y else false) true);
/* Count how many times function `pred' returns true for the elements
of `list'.
# Count how many times function `pred' returns true for the elements
# of `list'.
Example:
count (x: x == 3) [ 3 2 3 4 6 ]
=> 2
*/
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
/* Return a singleton list or an empty list, depending on a boolean
value. Useful when building lists with optional elements
(e.g. `++ optional (system == "i686-linux") flashplayer').
# Return a singleton list or an empty list, depending on a boolean
# value. Useful when building lists with optional elements
# (e.g. `++ optional (system == "i686-linux") flashplayer').
Example:
optional true "foo"
=> [ "foo" ]
optional false "foo"
=> [ ]
*/
optional = cond: elem: if cond then [elem] else [];
/* Return a list or an empty list, dependening on a boolean value.
# Return a list or an empty list, dependening on a boolean value.
Example:
optionals true [ 2 3 ]
=> [ 2 3 ]
optionals false [ 2 3 ]
=> [ ]
*/
optionals = cond: elems: if cond then elems else [];
# If argument is a list, return it; else, wrap it in a singleton
# list. If you're using this, you should almost certainly
# reconsider if there isn't a more "well-typed" approach.
/* If argument is a list, return it; else, wrap it in a singleton
list. If you're using this, you should almost certainly
reconsider if there isn't a more "well-typed" approach.
Example:
toList [ 1 2 ]
=> [ 1 2 ]
toList "hi"
=> [ "hi "]
*/
toList = x: if isList x then x else [x];
/* Return a list of integers from `first' up to and including `last'.
# Return a list of integers from `first' up to and including `last'.
Example:
range 2 4
=> [ 2 3 4 ]
range 3 2
=> [ ]
*/
range =
if builtins ? genList then
first: last:
@ -136,9 +228,13 @@ rec {
then []
else [first] ++ range (first + 1) last;
/* Splits the elements of a list in two lists, `right' and
`wrong', depending on the evaluation of a predicate.
# Partition the elements of a list in two lists, `right' and
# `wrong', depending on the evaluation of a predicate.
Example:
partition (x: x > 2) [ 5 1 2 3 4 ]
=> { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
*/
partition = pred:
fold (h: t:
if pred h
@ -146,7 +242,14 @@ rec {
else { right = t.right; wrong = [h] ++ t.wrong; }
) { right = []; wrong = []; };
/* Merges two lists of the same size together. If the sizes aren't the same
the merging stops at the shortest. How both lists are merged is defined
by the first argument.
Example:
zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
=> ["he" "lo"]
*/
zipListsWith =
if builtins ? genList then
f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd))
@ -161,21 +264,37 @@ rec {
else [];
in zipListsWith' 0;
/* Merges two lists of the same size together. If the sizes aren't the same
the merging stops at the shortest.
Example:
zipLists [ 1 2 ] [ "a" "b" ]
=> [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
*/
zipLists = zipListsWith (fst: snd: { inherit fst snd; });
/* Reverse the order of the elements of a list.
# Reverse the order of the elements of a list.
Example:
reverseList [ "b" "o" "j" ]
=> [ "j" "o" "b" ]
*/
reverseList =
if builtins ? genList then
xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l
else
fold (e: acc: acc ++ [ e ]) [];
/* Sort a list based on a comparator function which compares two
elements and returns true if the first argument is strictly below
the second argument. The returned list is sorted in an increasing
order. The implementation does a quick-sort.
# Sort a list based on a comparator function which compares two
# elements and returns true if the first argument is strictly below
# the second argument. The returned list is sorted in an increasing
# order. The implementation does a quick-sort.
Example:
sort (a: b: a < b) [ 5 3 7 ]
=> [ 3 5 7 ]
*/
sort = builtins.sort or (
strictLess: list:
let
@ -193,8 +312,14 @@ rec {
if len < 2 then list
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
/* Return the first (at most) N elements of a list.
# Return the first (at most) N elements of a list.
Example:
take 2 [ "a" "b" "c" "d" ]
=> [ "a" "b" ]
take 2 [ ]
=> [ ]
*/
take =
if builtins ? genList then
count: sublist 0 count
@ -209,8 +334,14 @@ rec {
[ (elemAt list n) ] ++ take' (n + 1);
in take' 0;
/* Remove the first (at most) N elements of a list.
# Remove the first (at most) N elements of a list.
Example:
drop 2 [ "a" "b" "c" "d" ]
=> [ "c" "d" ]
drop 2 [ ]
=> [ ]
*/
drop =
if builtins ? genList then
count: list: sublist count (length list) list
@ -225,9 +356,15 @@ rec {
drop' (n - 1) ++ [ (elemAt list n) ];
in drop' (len - 1);
/* Return a list consisting of at most count elements of list,
starting at index start.
# Return a list consisting of at most count elements of list,
# starting at index start.
Example:
sublist 1 3 [ "a" "b" "c" "d" "e" ]
=> [ "b" "c" "d" ]
sublist 1 3 [ ]
=> [ ]
*/
sublist = start: count: list:
let len = length list; in
genList
@ -236,23 +373,36 @@ rec {
else if start + count > len then len - start
else count);
/* Return the last element of a list.
# Return the last element of a list.
Example:
last [ 1 2 3 ]
=> 3
*/
last = list:
assert list != []; elemAt list (length list - 1);
/* Return all elements but the last
# Return all elements but the last
Example:
init [ 1 2 3 ]
=> [ 1 2 ]
*/
init = list: assert list != []; take (length list - 1) list;
deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
/* FIXME(zimbatm) Not used anywhere
*/
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
# Remove duplicate elements from the list. O(n^2) complexity.
/* Remove duplicate elements from the list. O(n^2) complexity.
Example:
unique [ 3 2 3 4 ]
=> [ 3 2 4 ]
*/
unique = list:
if list == [] then
[]
@ -262,12 +412,24 @@ rec {
xs = unique (drop 1 list);
in [x] ++ remove x xs;
/* Intersects list 'e' and another list. O(nm) complexity.
# Intersects list 'e' and another list. O(nm) complexity.
Example:
intersectLists [ 1 2 3 ] [ 6 3 2 ]
=> [ 3 2 ]
*/
intersectLists = e: filter (x: elem x e);
/* Subtracts list 'e' from another list. O(nm) complexity.
# Subtracts list 'e' from another list. O(nm) complexity.
Example:
subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
=> [ 1 4 5 ]
*/
subtractLists = e: filter (x: !(elem x e));
/*** deprecated stuff ***/
deepSeqList = throw "removed 2016-02-29 because unused and broken";
}

View file

@ -10,65 +10,147 @@ rec {
inherit (builtins) stringLength substring head tail isString replaceStrings;
/* Concatenate a list of strings.
# Concatenate a list of strings.
Example:
concatStrings ["foo" "bar"]
=> "foobar"
*/
concatStrings =
if builtins ? concatStringsSep then
builtins.concatStringsSep ""
else
lib.foldl' (x: y: x + y) "";
/* Map a function over a list and concatenate the resulting strings.
# Map a function over a list and concatenate the resulting strings.
Example:
concatMapStrings (x: "a" + x) ["foo" "bar"]
=> "afooabar"
*/
concatMapStrings = f: list: concatStrings (map f list);
/* Like `concatMapStrings' except that the f functions also gets the
position as a parameter.
Example:
concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"]
=> "1-foo2-bar"
*/
concatImapStrings = f: list: concatStrings (lib.imap f list);
/* Place an element between each element of a list
# Place an element between each element of a list, e.g.,
# `intersperse "," ["a" "b" "c"]' returns ["a" "," "b" "," "c"].
Example:
intersperse "/" ["usr" "local" "bin"]
=> ["usr" "/" "local" "/" "bin"].
*/
intersperse = separator: list:
if list == [] || length list == 1
then list
else tail (lib.concatMap (x: [separator x]) list);
/* Concatenate a list of strings with a separator between each element
# Concatenate a list of strings with a separator between each element, e.g.
# concatStringsSep " " ["foo" "bar" "xyzzy"] == "foo bar xyzzy"
Example:
concatStringsSep "/" ["usr" "local" "bin"]
=> "usr/local/bin"
*/
concatStringsSep = builtins.concatStringsSep or (separator: list:
concatStrings (intersperse separator list));
/* First maps over the list and then concatenates it.
Example:
concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"]
=> "FOO-BAR-BAZ"
*/
concatMapStringsSep = sep: f: list: concatStringsSep sep (map f list);
/* First imaps over the list and then concatenates it.
Example:
concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
=> "6-3-2"
*/
concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap f list);
/* Construct a Unix-style search path consisting of each `subDir"
directory of the given list of packages.
# Construct a Unix-style search path consisting of each `subDir"
# directory of the given list of packages. For example,
# `makeSearchPath "bin" ["x" "y" "z"]' returns "x/bin:y/bin:z/bin".
Example:
makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
=> "/root/bin:/usr/bin:/usr/local/bin"
makeSearchPath "bin" ["/"]
=> "//bin"
*/
makeSearchPath = subDir: packages:
concatStringsSep ":" (map (path: path + "/" + subDir) packages);
/* Construct a library search path (such as RPATH) containing the
libraries for a set of packages
# Construct a library search path (such as RPATH) containing the
# libraries for a set of packages, e.g. "${pkg1}/lib:${pkg2}/lib:...".
Example:
makeLibraryPath [ "/usr" "/usr/local" ]
=> "/usr/lib:/usr/local/lib"
pkgs = import <nixpkgs> { }
makeLibraryPath [ pkgs.openssl pkgs.zlib ]
=> "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib"
*/
makeLibraryPath = makeSearchPath "lib";
# Construct a binary search path (such as $PATH) containing the
# binaries for a set of packages, e.g. "${pkg1}/bin:${pkg2}/bin:...".
/* Construct a binary search path (such as $PATH) containing the
binaries for a set of packages.
Example:
makeBinPath ["/root" "/usr" "/usr/local"]
=> "/root/bin:/usr/bin:/usr/local/bin"
*/
makeBinPath = makeSearchPath "bin";
# Idem for Perl search paths.
/* Construct a perl search path (such as $PERL5LIB)
FIXME(zimbatm): this should be moved in perl-specific code
Example:
pkgs = import <nixpkgs> { }
makePerlPath [ pkgs.perlPackages.NetSMTP ]
=> "/nix/store/n0m1fk9c960d8wlrs62sncnadygqqc6y-perl-Net-SMTP-1.25/lib/perl5/site_perl"
*/
makePerlPath = makeSearchPath "lib/perl5/site_perl";
/* Dependening on the boolean `cond', return either the given string
or the empty string. Useful to contatenate against a bigger string.
# Dependening on the boolean `cond', return either the given string
# or the empty string.
Example:
optionalString true "some-string"
=> "some-string"
optionalString false "some-string"
=> ""
*/
optionalString = cond: string: if cond then string else "";
/* Determine whether a string has given prefix.
# Determine whether a string has given prefix/suffix.
Example:
hasPrefix "foo" "foobar"
=> true
hasPrefix "foo" "barfoo"
=> false
*/
hasPrefix = pref: str:
substring 0 (stringLength pref) str == pref;
/* Determine whether a string has given suffix.
Example:
hasSuffix "foo" "foobar"
=> false
hasSuffix "foo" "barfoo"
=> true
*/
hasSuffix = suff: str:
let
lenStr = stringLength str;
@ -76,36 +158,55 @@ rec {
in lenStr >= lenSuff &&
substring (lenStr - lenSuff) lenStr str == suff;
/* Convert a string to a list of characters (i.e. singleton strings).
This allows you to, e.g., map a function over each character. However,
note that this will likely be horribly inefficient; Nix is not a
general purpose programming language. Complex string manipulations
should, if appropriate, be done in a derivation.
Also note that Nix treats strings as a list of bytes and thus doesn't
handle unicode.
# Convert a string to a list of characters (i.e. singleton strings).
# For instance, "abc" becomes ["a" "b" "c"]. This allows you to,
# e.g., map a function over each character. However, note that this
# will likely be horribly inefficient; Nix is not a general purpose
# programming language. Complex string manipulations should, if
# appropriate, be done in a derivation.
Example:
stringToCharacters ""
=> [ ]
stringToCharacters "abc"
=> [ "a" "b" "c" ]
stringToCharacters "💩"
=> [ "<EFBFBD>" "<EFBFBD>" "<EFBFBD>" "<EFBFBD>" ]
*/
stringToCharacters = s:
map (p: substring p 1 s) (lib.range 0 (stringLength s - 1));
/* Manipulate a string character by character and replace them by
strings before concatenating the results.
# Manipulate a string charactter by character and replace them by
# strings before concatenating the results.
Example:
stringAsChars (x: if x == "a" then "i" else x) "nax"
=> "nix"
*/
stringAsChars = f: s:
concatStrings (
map f (stringToCharacters s)
);
/* Escape occurrence of the elements of list in string by
prefixing it with a backslash.
# Escape occurrence of the elements of list in string by
# prefixing it with a backslash. For example, escape ["(" ")"]
# "(foo)" returns the string \(foo\).
Example:
escape ["(" ")"] "(foo)"
=> "\\(foo\\)"
*/
escape = list: replaceChars list (map (c: "\\${c}") list);
/* Escape all characters that have special meaning in the Bourne shell.
# Escape all characters that have special meaning in the Bourne shell.
Example:
escapeShellArg "so([<>])me"
=> "so\\(\\[\\<\\>\\]\\)me"
*/
escapeShellArg = lib.escape (stringToCharacters "\\ ';$`()|<>\t*[]");
# Obsolete - use replaceStrings instead.
/* Obsolete - use replaceStrings instead. */
replaceChars = builtins.replaceStrings or (
del: new: s:
let
@ -119,21 +220,52 @@ rec {
in
stringAsChars subst s);
# Case conversion utilities.
lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/* Converts an ASCII string to lower-case.
Example:
toLower "HOME"
=> "home"
*/
toLower = replaceChars upperChars lowerChars;
/* Converts an ASCII string to upper-case.
Example:
toLower "home"
=> "HOME"
*/
toUpper = replaceChars lowerChars upperChars;
/* Appends string context from another string. This is an implementation
detail of Nix.
# Appends string context from another string.
Strings in Nix carry an invisible `context' which is a list of strings
representing store paths. If the string is later used in a derivation
attribute, the derivation will properly populate the inputDrvs and
inputSrcs.
Example:
pkgs = import <nixpkgs> { };
addContextFrom pkgs.coreutils "bar"
=> "bar"
*/
addContextFrom = a: b: substring 0 0 a + b;
/* Cut a string with a separator and produces a list of strings which
were separated by this separator.
# Cut a string with a separator and produces a list of strings which
# were separated by this separator; e.g., `splitString "."
# "foo.bar.baz"' returns ["foo" "bar" "baz"].
NOTE: this function is not performant and should be avoided
Example:
splitString "." "foo.bar.baz"
=> [ "foo" "bar" "baz" ]
splitString "/" "/usr/local/bin"
=> [ "" "usr" "local" "bin" ]
*/
splitString = _sep: _s:
let
sep = addContextFrom _s _sep;
@ -157,10 +289,15 @@ rec {
in
recurse 0 0;
/* Return the suffix of the second argument if the first argument matches
its prefix.
# return the suffix of the second argument if the first argument match its
# prefix. e.g.,
# `removePrefix "foo." "foo.bar.baz"' returns "bar.baz".
Example:
removePrefix "foo." "foo.bar.baz"
=> "bar.baz"
removePrefix "xxx" "foo.bar.baz"
=> "foo.bar.baz"
*/
removePrefix = pre: s:
let
preLen = stringLength pre;
@ -171,6 +308,15 @@ rec {
else
s;
/* Return the prefix of the second argument if the first argument matches
its suffix.
Example:
removeSuffix "front" "homefront"
=> "home"
removeSuffix "xxx" "homefront"
=> "homefront"
*/
removeSuffix = suf: s:
let
sufLen = stringLength suf;
@ -181,25 +327,49 @@ rec {
else
s;
# Return true iff string v1 denotes a version older than v2.
/* Return true iff string v1 denotes a version older than v2.
Example:
versionOlder "1.1" "1.2"
=> true
versionOlder "1.1" "1.1"
=> false
*/
versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1;
/* Return true iff string v1 denotes a version equal to or newer than v2.
# Return true iff string v1 denotes a version equal to or newer than v2.
Example:
versionAtLeast "1.1" "1.0"
=> true
versionAtLeast "1.1" "1.1"
=> true
versionAtLeast "1.1" "1.2"
=> false
*/
versionAtLeast = v1: v2: !versionOlder v1 v2;
/* This function takes an argument that's either a derivation or a
derivation's "name" attribute and extracts the version part from that
argument.
# This function takes an argument that's either a derivation or a
# derivation's "name" attribute and extracts the version part from that
# argument. For example:
#
# lib.getVersion "youtube-dl-2016.01.01" ==> "2016.01.01"
# lib.getVersion pkgs.youtube-dl ==> "2016.01.01"
Example:
getVersion "youtube-dl-2016.01.01"
=> "2016.01.01"
getVersion pkgs.youtube-dl
=> "2016.01.01"
*/
getVersion = x: (builtins.parseDrvName (x.name or x)).version;
/* Extract name with version from URL. Ask for separator which is
supposed to start extension.
# Extract name with version from URL. Ask for separator which is
# supposed to start extension.
Example:
nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
=> "nix"
nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
=> "nix-1.7-x86"
*/
nameFromURL = url: sep:
let
components = splitString "/" url;
@ -207,14 +377,24 @@ rec {
name = builtins.head (splitString sep filename);
in assert name != filename; name;
/* Create an --{enable,disable}-<feat> string that can be passed to
standard GNU Autoconf scripts.
# Create an --{enable,disable}-<feat> string that can be passed to
# standard GNU Autoconf scripts.
Example:
enableFeature true "shared"
=> "--enable-shared"
enableFeature false "shared"
=> "--disable-shared"
*/
enableFeature = enable: feat: "--${if enable then "enable" else "disable"}-${feat}";
/* Create a fixed width string with additional prefix to match
required width.
# Create a fixed width string with additional prefix to match
# required width.
Example:
fixedWidthString 5 "0" (toString 15)
=> "00015"
*/
fixedWidthString = width: filler: str:
let
strw = lib.stringLength str;
@ -223,25 +403,58 @@ rec {
assert strw <= width;
if strw == width then str else filler + fixedWidthString reqWidth filler str;
/* Format a number adding leading zeroes up to fixed width.
# Format a number adding leading zeroes up to fixed width.
Example:
fixedWidthNumber 5 15
=> "00015"
*/
fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
/* Check whether a value is a store path.
# Check whether a value is a store path.
Example:
isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
=> false
isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/"
=> true
isStorePath pkgs.python
=> true
*/
isStorePath = x: builtins.substring 0 1 (toString x) == "/" && dirOf (builtins.toPath x) == builtins.storeDir;
# Convert string to int
# Obviously, it is a bit hacky to use fromJSON that way.
/* Convert string to int
Obviously, it is a bit hacky to use fromJSON that way.
Example:
toInt "1337"
=> 1337
toInt "-4"
=> -4
toInt "3.14"
=> error: floating point JSON numbers are not supported
*/
toInt = str:
let may_be_int = builtins.fromJSON str; in
if builtins.isInt may_be_int
then may_be_int
else throw "Could not convert ${str} to int.";
# Read a list of paths from `file', relative to the `rootPath'. Lines
# beginning with `#' are treated as comments and ignored. Whitespace
# is significant.
/* Read a list of paths from `file', relative to the `rootPath'. Lines
beginning with `#' are treated as comments and ignored. Whitespace
is significant.
NOTE: this function is not performant and should be avoided
Example:
readPathsFromFile /prefix
./pkgs/development/libraries/qt-5/5.4/qtbase/series
=> [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
"/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
"/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
"/prefix/nix-profiles-library-paths.patch"
"/prefix/compose-search-path.patch" ]
*/
readPathsFromFile = rootPath: file:
let
root = toString rootPath;
@ -253,5 +466,4 @@ rec {
absolutePaths = builtins.map (path: builtins.toPath (root + "/" + path)) relativePaths;
in
absolutePaths;
}