2 String manipulation functions.
7 inherit (builtins) length;
9 inherit (lib.trivial) warnIf;
11 asciiTable = import ./ascii-table.nix;
41 unsafeDiscardStringContext
45 Concatenate a list of strings.
50 concatStrings :: [string] -> string
55 ## `lib.strings.concatStrings` usage example
58 concatStrings ["foo" "bar"]
64 concatStrings = builtins.concatStringsSep "";
67 Map a function over a list and concatenate the resulting strings.
73 : 1\. Function argument
76 : 2\. Function argument
81 concatMapStrings :: (a -> string) -> [a] -> string
86 ## `lib.strings.concatMapStrings` usage example
89 concatMapStrings (x: "a" + x) ["foo" "bar"]
95 concatMapStrings = f: list: concatStrings (map f list);
98 Like `concatMapStrings` except that the f functions also gets the
99 position as a parameter.
105 : 1\. Function argument
108 : 2\. Function argument
113 concatImapStrings :: (int -> a -> string) -> [a] -> string
118 ## `lib.strings.concatImapStrings` usage example
121 concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"]
127 concatImapStrings = f: list: concatStrings (lib.imap1 f list);
130 Place an element between each element of a list
136 : Separator to add between elements
144 intersperse :: a -> [a] -> [a]
149 ## `lib.strings.intersperse` usage example
152 intersperse "/" ["usr" "local" "bin"]
153 => ["usr" "/" "local" "/" "bin"].
161 if list == [] || length list == 1
163 else tail (lib.concatMap (x: [separator x]) list);
166 Concatenate a list of strings with a separator between each element
171 : Separator to add between elements
174 : List of input strings
179 concatStringsSep :: string -> [string] -> string
184 ## `lib.strings.concatStringsSep` usage example
187 concatStringsSep "/" ["usr" "local" "bin"]
193 concatStringsSep = builtins.concatStringsSep;
196 Maps a function over a list of strings and then concatenates the
197 result with the specified separator interspersed between
204 : Separator to add between elements
207 : Function to map over the list
210 : List of input strings
215 concatMapStringsSep :: string -> (a -> string) -> [a] -> string
220 ## `lib.strings.concatMapStringsSep` usage example
223 concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"]
229 concatMapStringsSep =
232 list: concatStringsSep sep (map f list);
235 Same as `concatMapStringsSep`, but the mapping function
236 additionally receives the position of its argument.
242 : Separator to add between elements
245 : Function that receives elements and their positions
248 : List of input strings
253 concatIMapStringsSep :: string -> (int -> a -> string) -> [a] -> string
258 ## `lib.strings.concatImapStringsSep` usage example
261 concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
267 concatImapStringsSep =
270 list: concatStringsSep sep (lib.imap1 f list);
273 Concatenate a list of strings, adding a newline at the end of each one.
274 Defined as `concatMapStrings (s: s + "\n")`.
279 : List of strings. Any element that is not a string will be implicitly converted to a string.
284 concatLines :: [string] -> string
289 ## `lib.strings.concatLines` usage example
292 concatLines [ "foo" "bar" ]
298 concatLines = concatMapStrings (s: s + "\n");
301 Repeat a string `n` times,
302 and concatenate the parts into a new string.
308 : 1\. Function argument
311 : 2\. Function argument
316 replicate :: int -> string -> string
321 ## `lib.strings.replicate` usage example
327 => "hellohellohellohellohello"
332 replicate = n: s: concatStrings (lib.lists.replicate n s);
335 Remove leading and trailing whitespace from a string `s`.
337 Whitespace is defined as any of the following characters:
348 trim :: string -> string
353 ## `lib.strings.trim` usage example
356 trim " hello, world! "
368 Remove leading and/or trailing whitespace from a string `s`.
370 To remove both leading and trailing whitespace, you can also use [`trim`](#function-library-lib.strings.trim)
372 Whitespace is defined as any of the following characters:
377 `config` (Attribute set)
379 : Whether to trim leading whitespace (`false` by default)
382 : Whether to trim trailing whitespace (`false` by default)
390 trimWith :: { start :: Bool; end :: Bool } -> String -> String
395 ## `lib.strings.trimWith` usage example
398 trimWith { start = true; } " hello, world! "}
401 trimWith { end = true; } " hello, world! "}
413 # Define our own whitespace character class instead of using
414 # `[:space:]`, which is not well-defined.
417 # To match up until trailing whitespace, we need to capture a
418 # group that ends with a non-whitespace character.
421 "[${chars}]*(.*[^${chars}])[${chars}]*"
425 "(.*[^${chars}])[${chars}]*"
429 # If the string was empty or entirely whitespace,
430 # then the regex may not match and `res` will be `null`.
433 optionalString (res != null) (head res);
436 Construct a Unix-style, colon-separated search path consisting of
437 the given `subDir` appended to each of the given paths.
442 : Directory name to append
450 makeSearchPath :: string -> [string] -> string
455 ## `lib.strings.makeSearchPath` usage example
458 makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
459 => "/root/bin:/usr/bin:/usr/local/bin"
460 makeSearchPath "bin" [""]
469 concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths));
472 Construct a Unix-style search path by appending the given
473 `subDir` to the specified `output` of each of the packages.
475 If no output by the given name is found, fallback to `.out` and then to
482 : Package output to use
485 : Directory name to append
493 makeSearchPathOutput :: string -> string -> [package] -> string
498 ## `lib.strings.makeSearchPathOutput` usage example
501 makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ]
502 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin"
507 makeSearchPathOutput =
510 pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs);
513 Construct a library search path (such as RPATH) containing the
514 libraries for a set of packages
524 makeLibraryPath :: [package] -> string
529 ## `lib.strings.makeLibraryPath` usage example
532 makeLibraryPath [ "/usr" "/usr/local" ]
533 => "/usr/lib:/usr/local/lib"
534 pkgs = import <nixpkgs> { }
535 makeLibraryPath [ pkgs.openssl pkgs.zlib ]
536 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib"
541 makeLibraryPath = makeSearchPathOutput "lib" "lib";
544 Construct an include search path (such as C_INCLUDE_PATH) containing the
545 header files for a set of packages or paths.
555 makeIncludePath :: [package] -> string
560 ## `lib.strings.makeIncludePath` usage example
563 makeIncludePath [ "/usr" "/usr/local" ]
564 => "/usr/include:/usr/local/include"
565 pkgs = import <nixpkgs> { }
566 makeIncludePath [ pkgs.openssl pkgs.zlib ]
567 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/include:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8-dev/include"
572 makeIncludePath = makeSearchPathOutput "dev" "include";
575 Construct a binary search path (such as $PATH) containing the
576 binaries for a set of packages.
586 makeBinPath :: [package] -> string
591 ## `lib.strings.makeBinPath` usage example
594 makeBinPath ["/root" "/usr" "/usr/local"]
595 => "/root/bin:/usr/bin:/usr/local/bin"
600 makeBinPath = makeSearchPathOutput "bin" "bin";
603 Normalize path, removing extraneous /s
609 : 1\. Function argument
614 normalizePath :: string -> string
619 ## `lib.strings.normalizePath` usage example
622 normalizePath "/a//b///c/"
632 lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported.
633 Path values are always normalised in Nix, so there's no need to call this function on them.
634 This function also copies the path to the Nix store and returns the store path, the same as "''${path}" will, which may not be what you want.
635 This behavior is deprecated and will throw an error in the future.''
638 (x: y: if y == "/" && hasSuffix "/" x then x else x+y)
640 (stringToCharacters s)
644 Depending on the boolean `cond', return either the given string
645 or the empty string. Useful to concatenate against a bigger string.
654 : String to return if condition is true
659 optionalString :: bool -> string -> string
664 ## `lib.strings.optionalString` usage example
667 optionalString true "some-string"
669 optionalString false "some-string"
677 string: if cond then string else "";
680 Determine whether a string has given prefix.
686 : Prefix to check for
694 hasPrefix :: string -> string -> bool
699 ## `lib.strings.hasPrefix` usage example
702 hasPrefix "foo" "foobar"
704 hasPrefix "foo" "barfoo"
713 # Before 23.05, paths would be copied to the store before converting them
714 # to strings and comparing. This was surprising and confusing.
718 lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported.
719 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
720 This function also copies the path to the Nix store, which may not be what you want.
721 This behavior is deprecated and will throw an error in the future.
722 You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.''
723 (substring 0 (stringLength pref) str == pref);
726 Determine whether a string has given suffix.
732 : Suffix to check for
740 hasSuffix :: string -> string -> bool
745 ## `lib.strings.hasSuffix` usage example
748 hasSuffix "foo" "foobar"
750 hasSuffix "foo" "barfoo"
760 lenContent = stringLength content;
761 lenSuffix = stringLength suffix;
763 # Before 23.05, paths would be copied to the store before converting them
764 # to strings and comparing. This was surprising and confusing.
768 lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
769 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
770 This function also copies the path to the Nix store, which may not be what you want.
771 This behavior is deprecated and will throw an error in the future.''
773 lenContent >= lenSuffix
774 && substring (lenContent - lenSuffix) lenContent content == suffix
778 Determine whether a string contains the given infix
784 : 1\. Function argument
787 : 2\. Function argument
792 hasInfix :: string -> string -> bool
797 ## `lib.strings.hasInfix` usage example
806 hasInfix "foo" "abcd"
812 hasInfix = infix: content:
813 # Before 23.05, paths would be copied to the store before converting them
814 # to strings and comparing. This was surprising and confusing.
818 lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported.
819 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
820 This function also copies the path to the Nix store, which may not be what you want.
821 This behavior is deprecated and will throw an error in the future.''
822 (builtins.match ".*${escapeRegex infix}.*" "${content}" != null);
825 Convert a string `s` to a list of characters (i.e. singleton strings).
826 This allows you to, e.g., map a function over each character. However,
827 note that this will likely be horribly inefficient; Nix is not a
828 general purpose programming language. Complex string manipulations
829 should, if appropriate, be done in a derivation.
830 Also note that Nix treats strings as a list of bytes and thus doesn't
837 : 1\. Function argument
842 stringToCharacters :: string -> [string]
847 ## `lib.strings.stringToCharacters` usage example
850 stringToCharacters ""
852 stringToCharacters "abc"
854 stringToCharacters "🦄"
855 => [ "�" "�" "�" "�" ]
860 stringToCharacters = s:
861 genList (p: substring p 1 s) (stringLength s);
864 Manipulate a string character by character and replace them by
865 strings before concatenating the results.
871 : Function to map over each individual character
879 stringAsChars :: (string -> string) -> string -> string
884 ## `lib.strings.stringAsChars` usage example
887 stringAsChars (x: if x == "a" then "i" else x) "nax"
894 # Function to map over each individual character
898 map f (stringToCharacters s)
902 Convert char to ascii value, must be in printable range
908 : 1\. Function argument
913 charToInt :: string -> int
918 ## `lib.strings.charToInt` usage example
929 charToInt = c: builtins.getAttr c asciiTable;
932 Escape occurrence of the elements of `list` in `string` by
933 prefixing it with a backslash.
939 : 1\. Function argument
942 : 2\. Function argument
947 escape :: [string] -> string -> string
952 ## `lib.strings.escape` usage example
955 escape ["(" ")"] "(foo)"
961 escape = list: replaceStrings list (map (c: "\\${c}") list);
964 Escape occurrence of the element of `list` in `string` by
965 converting to its ASCII value and prefixing it with \\x.
966 Only works for printable ascii characters.
972 : 1\. Function argument
975 : 2\. Function argument
980 escapeC = [string] -> string -> string
985 ## `lib.strings.escapeC` usage example
988 escapeC [" "] "foo bar"
994 escapeC = list: replaceStrings list (map (c: "\\x${ toLower (lib.toHexString (charToInt c))}") list);
997 Escape the `string` so it can be safely placed inside a URL
1003 : 1\. Function argument
1008 escapeURL :: string -> string
1013 ## `lib.strings.escapeURL` usage example
1016 escapeURL "foo/bar baz"
1017 => "foo%2Fbar%20baz"
1023 unreserved = [ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "-" "_" "." "~" ];
1024 toEscape = builtins.removeAttrs asciiTable unreserved;
1026 replaceStrings (builtins.attrNames toEscape) (lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") toEscape);
1029 Quote `string` to be used safely within the Bourne shell.
1035 : 1\. Function argument
1040 escapeShellArg :: string -> string
1045 ## `lib.strings.escapeShellArg` usage example
1048 escapeShellArg "esc'ape\nme"
1049 => "'esc'\\''ape\nme'"
1054 escapeShellArg = arg: "'${replaceStrings ["'"] ["'\\''"] (toString arg)}'";
1057 Quote all arguments to be safely passed to the Bourne shell.
1062 : 1\. Function argument
1067 escapeShellArgs :: [string] -> string
1072 ## `lib.strings.escapeShellArgs` usage example
1075 escapeShellArgs ["one" "two three" "four'five"]
1076 => "'one' 'two three' 'four'\\''five'"
1081 escapeShellArgs = concatMapStringsSep " " escapeShellArg;
1084 Test whether the given `name` is a valid POSIX shell variable name.
1090 : 1\. Function argument
1100 ## `lib.strings.isValidPosixName` usage example
1103 isValidPosixName "foo_bar000"
1105 isValidPosixName "0-bad.jpg"
1111 isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
1114 Translate a Nix value into a shell variable declaration, with proper escaping.
1116 The value can be a string (mapped to a regular variable), a list of strings
1117 (mapped to a Bash-style array) or an attribute set of strings (mapped to a
1118 Bash-style associative array). Note that "string" includes string-coercible
1119 values like paths or derivations.
1121 Strings are translated into POSIX sh-compatible code; lists and attribute sets
1122 assume a shell that understands Bash syntax (e.g. Bash or ZSH).
1128 : 1\. Function argument
1131 : 2\. Function argument
1136 string -> ( string | [string] | { ${name} :: string; } ) -> string
1141 ## `lib.strings.toShellVar` usage example
1145 ${toShellVar "foo" "some string"}
1146 [[ "$foo" == "some string" ]]
1152 toShellVar = name: value:
1153 lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
1154 if isAttrs value && ! isStringLike value then
1155 "declare -A ${name}=(${
1156 concatStringsSep " " (lib.mapAttrsToList (n: v:
1157 "[${escapeShellArg n}]=${escapeShellArg v}"
1160 else if isList value then
1161 "declare -a ${name}=(${escapeShellArgs value})"
1163 "${name}=${escapeShellArg value}"
1167 Translate an attribute set `vars` into corresponding shell variable declarations
1174 : 1\. Function argument
1180 ${name} :: string | [ string ] | { ${key} :: string; };
1186 ## `lib.strings.toShellVars` usage example
1193 ${toShellVars { inherit foo bar; }}
1194 [[ "$foo" == "$bar" ]]
1200 toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
1203 Turn a string `s` into a Nix expression representing that string
1208 : 1\. Function argument
1213 escapeNixString :: string -> string
1218 ## `lib.strings.escapeNixString` usage example
1221 escapeNixString "hello\${}\n"
1222 => "\"hello\\\${}\\n\""
1227 escapeNixString = s: escape ["$"] (toJSON s);
1230 Turn a string `s` into an exact regular expression
1235 : 1\. Function argument
1240 escapeRegex :: string -> string
1245 ## `lib.strings.escapeRegex` usage example
1248 escapeRegex "[^a-z]*"
1254 escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
1257 Quotes a string `s` if it can't be used as an identifier directly.
1263 : 1\. Function argument
1268 escapeNixIdentifier :: string -> string
1273 ## `lib.strings.escapeNixIdentifier` usage example
1276 escapeNixIdentifier "hello"
1278 escapeNixIdentifier "0abc"
1284 escapeNixIdentifier = s:
1285 # Regex from https://github.com/NixOS/nix/blob/d048577909e383439c2549e849c5c2f2016c997e/src/libexpr/lexer.l#L91
1286 if match "[a-zA-Z_][a-zA-Z0-9_'-]*" s != null
1287 then s else escapeNixString s;
1290 Escapes a string `s` such that it is safe to include verbatim in an XML
1296 : 1\. Function argument
1301 escapeXML :: string -> string
1306 ## `lib.strings.escapeXML` usage example
1309 escapeXML ''"test" 'test' < & >''
1310 => ""test" 'test' < & >"
1315 escapeXML = builtins.replaceStrings
1316 ["\"" "'" "<" ">" "&"]
1317 [""" "'" "<" ">" "&"];
1319 # warning added 12-12-2022
1320 replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
1322 # Case conversion utilities.
1323 lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
1324 upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1327 Converts an ASCII string `s` to lower-case.
1332 : The string to convert to lower-case.
1337 toLower :: string -> string
1342 ## `lib.strings.toLower` usage example
1351 toLower = replaceStrings upperChars lowerChars;
1354 Converts an ASCII string `s` to upper-case.
1359 : The string to convert to upper-case.
1365 toUpper :: string -> string
1370 ## `lib.strings.toUpper` usage example
1379 toUpper = replaceStrings lowerChars upperChars;
1382 Appends string context from string like object `src` to `target`.
1385 This is an implementation
1386 detail of Nix and should be used carefully.
1389 Strings in Nix carry an invisible `context` which is a list of strings
1390 representing store paths. If the string is later used in a derivation
1391 attribute, the derivation will properly populate the inputDrvs and
1398 : The string to take the context from. If the argument is not a string,
1399 it will be implicitly converted to a string.
1402 : The string to append the context to. If the argument is not a string,
1403 it will be implicitly converted to a string.
1408 addContextFrom :: string -> string -> string
1413 ## `lib.strings.addContextFrom` usage example
1416 pkgs = import <nixpkgs> { };
1417 addContextFrom pkgs.coreutils "bar"
1421 The context can be displayed using the `toString` function:
1424 nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
1426 "/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
1432 addContextFrom = src: target: substring 0 0 src + target;
1435 Cut a string with a separator and produces a list of strings which
1436 were separated by this separator.
1441 : 1\. Function argument
1444 : 2\. Function argument
1449 splitString :: string -> string -> [string]
1454 ## `lib.strings.splitString` usage example
1457 splitString "." "foo.bar.baz"
1458 => [ "foo" "bar" "baz" ]
1459 splitString "/" "/usr/local/bin"
1460 => [ "" "usr" "local" "bin" ]
1465 splitString = sep: s:
1467 splits = builtins.filter builtins.isString (builtins.split (escapeRegex (toString sep)) (toString s));
1469 map (addContextFrom s) splits;
1473 Return a string without the specified prefix, if the prefix matches.
1478 : Prefix to remove if it matches
1486 removePrefix :: string -> string -> string
1491 ## `lib.strings.removePrefix` usage example
1494 removePrefix "foo." "foo.bar.baz"
1496 removePrefix "xxx" "foo.bar.baz"
1505 # Before 23.05, paths would be copied to the store before converting them
1506 # to strings and comparing. This was surprising and confusing.
1510 lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported.
1511 There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case.
1512 This function also copies the path to the Nix store, which may not be what you want.
1513 This behavior is deprecated and will throw an error in the future.''
1515 preLen = stringLength prefix;
1517 if substring 0 preLen str == prefix then
1518 # -1 will take the string until the end
1519 substring preLen (-1) str
1524 Return a string without the specified suffix, if the suffix matches.
1530 : Suffix to remove if it matches
1538 removeSuffix :: string -> string -> string
1543 ## `lib.strings.removeSuffix` usage example
1546 removeSuffix "front" "homefront"
1548 removeSuffix "xxx" "homefront"
1557 # Before 23.05, paths would be copied to the store before converting them
1558 # to strings and comparing. This was surprising and confusing.
1562 lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
1563 There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case.
1564 This function also copies the path to the Nix store, which may not be what you want.
1565 This behavior is deprecated and will throw an error in the future.''
1567 sufLen = stringLength suffix;
1568 sLen = stringLength str;
1570 if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then
1571 substring 0 (sLen - sufLen) str
1576 Return true if string `v1` denotes a version older than `v2`.
1582 : 1\. Function argument
1585 : 2\. Function argument
1590 versionOlder :: String -> String -> Bool
1595 ## `lib.strings.versionOlder` usage example
1598 versionOlder "1.1" "1.2"
1600 versionOlder "1.1" "1.1"
1606 versionOlder = v1: v2: compareVersions v2 v1 == 1;
1609 Return true if string v1 denotes a version equal to or newer than v2.
1615 : 1\. Function argument
1618 : 2\. Function argument
1623 versionAtLeast :: String -> String -> Bool
1628 ## `lib.strings.versionAtLeast` usage example
1631 versionAtLeast "1.1" "1.0"
1633 versionAtLeast "1.1" "1.1"
1635 versionAtLeast "1.1" "1.2"
1641 versionAtLeast = v1: v2: !versionOlder v1 v2;
1644 This function takes an argument `x` that's either a derivation or a
1645 derivation's "name" attribute and extracts the name part from that
1651 : 1\. Function argument
1656 getName :: String | Derivation -> String
1662 ## `lib.strings.getName` usage example
1665 getName "youtube-dl-2016.01.01"
1667 getName pkgs.youtube-dl
1674 parse = drv: (parseDrvName drv).name;
1678 else x.pname or (parse x.name);
1681 This function takes an argument `x` that's either a derivation or a
1682 derivation's "name" attribute and extracts the version part from that
1689 : 1\. Function argument
1694 getVersion :: String | Derivation -> String
1699 ## `lib.strings.getVersion` usage example
1702 getVersion "youtube-dl-2016.01.01"
1704 getVersion pkgs.youtube-dl
1711 parse = drv: (parseDrvName drv).version;
1715 else x.version or (parse x.name);
1718 Extract name and version from a URL as shown in the examples.
1720 Separator `sep` is used to determine the end of the extension.
1726 : 1\. Function argument
1729 : 2\. Function argument
1734 nameFromURL :: String -> String
1739 ## `lib.strings.nameFromURL` usage example
1742 nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
1744 nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
1750 nameFromURL = url: sep:
1752 components = splitString "/" url;
1753 filename = lib.last components;
1754 name = head (splitString sep filename);
1755 in assert name != filename; name;
1758 Create a `"-D<feature>:<type>=<value>"` string that can be passed to typical
1764 : The feature to be set
1767 : The type of the feature to be set, as described in
1768 https://cmake.org/cmake/help/latest/command/set.html
1769 the possible values (case insensitive) are:
1770 BOOL FILEPATH PATH STRING INTERNAL
1778 cmakeOptionType :: string -> string -> string -> string
1783 ## `lib.strings.cmakeOptionType` usage example
1786 cmakeOptionType "string" "ENGINE" "sdl2"
1787 => "-DENGINE:STRING=sdl2"
1792 cmakeOptionType = let
1793 types = [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ];
1794 in type: feature: value:
1795 assert (elem (toUpper type) types);
1796 assert (isString feature);
1797 assert (isString value);
1798 "-D${feature}:${toUpper type}=${value}";
1801 Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
1808 : The condition to be made true or false
1811 : The controlling flag of the condition
1816 cmakeBool :: string -> bool -> string
1821 ## `lib.strings.cmakeBool` usage example
1824 cmakeBool "ENABLE_STATIC_LIBS" false
1825 => "-DENABLESTATIC_LIBS:BOOL=FALSE"
1830 cmakeBool = condition: flag:
1831 assert (lib.isString condition);
1832 assert (lib.isBool flag);
1833 cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag));
1836 Create a -D<feature>:STRING=<value> string that can be passed to typical
1838 This is the most typical usage, so it deserves a special case.
1844 : The feature to be set
1853 cmakeFeature :: string -> string -> string
1858 ## `lib.strings.cmakeFeature` usage example
1861 cmakeFeature "MODULES" "badblock"
1862 => "-DMODULES:STRING=badblock"
1867 cmakeFeature = feature: value:
1868 assert (lib.isString feature);
1869 assert (lib.isString value);
1870 cmakeOptionType "string" feature value;
1873 Create a -D<feature>=<value> string that can be passed to typical Meson
1880 : The feature to be set
1888 mesonOption :: string -> string -> string
1893 ## `lib.strings.mesonOption` usage example
1896 mesonOption "engine" "opengl"
1897 => "-Dengine=opengl"
1902 mesonOption = feature: value:
1903 assert (lib.isString feature);
1904 assert (lib.isString value);
1905 "-D${feature}=${value}";
1908 Create a -D<condition>={true,false} string that can be passed to typical
1915 : The condition to be made true or false
1918 : The controlling flag of the condition
1923 mesonBool :: string -> bool -> string
1928 ## `lib.strings.mesonBool` usage example
1931 mesonBool "hardened" true
1932 => "-Dhardened=true"
1933 mesonBool "static" false
1939 mesonBool = condition: flag:
1940 assert (lib.isString condition);
1941 assert (lib.isBool flag);
1942 mesonOption condition (lib.boolToString flag);
1945 Create a -D<feature>={enabled,disabled} string that can be passed to
1946 typical Meson invocations.
1952 : The feature to be enabled or disabled
1955 : The controlling flag
1960 mesonEnable :: string -> bool -> string
1965 ## `lib.strings.mesonEnable` usage example
1968 mesonEnable "docs" true
1970 mesonEnable "savage" false
1971 => "-Dsavage=disabled"
1976 mesonEnable = feature: flag:
1977 assert (lib.isString feature);
1978 assert (lib.isBool flag);
1979 mesonOption feature (if flag then "enabled" else "disabled");
1982 Create an --{enable,disable}-<feature> string that can be passed to
1983 standard GNU Autoconf scripts.
1989 : 1\. Function argument
1992 : 2\. Function argument
1997 enableFeature :: bool -> string -> string
2002 ## `lib.strings.enableFeature` usage example
2005 enableFeature true "shared"
2006 => "--enable-shared"
2007 enableFeature false "shared"
2008 => "--disable-shared"
2013 enableFeature = flag: feature:
2014 assert lib.isBool flag;
2015 assert lib.isString feature; # e.g. passing openssl instead of "openssl"
2016 "--${if flag then "enable" else "disable"}-${feature}";
2019 Create an --{enable-<feature>=<value>,disable-<feature>} string that can be passed to
2020 standard GNU Autoconf scripts.
2026 : 1\. Function argument
2029 : 2\. Function argument
2032 : 3\. Function argument
2037 enableFeatureAs :: bool -> string -> string -> string
2042 ## `lib.strings.enableFeatureAs` usage example
2045 enableFeatureAs true "shared" "foo"
2046 => "--enable-shared=foo"
2047 enableFeatureAs false "shared" (throw "ignored")
2048 => "--disable-shared"
2053 enableFeatureAs = flag: feature: value:
2054 enableFeature flag feature + optionalString flag "=${value}";
2057 Create an --{with,without}-<feature> string that can be passed to
2058 standard GNU Autoconf scripts.
2064 : 1\. Function argument
2067 : 2\. Function argument
2073 withFeature :: bool -> string -> string
2078 ## `lib.strings.withFeature` usage example
2081 withFeature true "shared"
2083 withFeature false "shared"
2084 => "--without-shared"
2089 withFeature = flag: feature:
2090 assert isString feature; # e.g. passing openssl instead of "openssl"
2091 "--${if flag then "with" else "without"}-${feature}";
2094 Create an --{with-<feature>=<value>,without-<feature>} string that can be passed to
2095 standard GNU Autoconf scripts.
2101 : 1\. Function argument
2104 : 2\. Function argument
2107 : 3\. Function argument
2112 withFeatureAs :: bool -> string -> string -> string
2118 ## `lib.strings.withFeatureAs` usage example
2121 withFeatureAs true "shared" "foo"
2122 => "--with-shared=foo"
2123 withFeatureAs false "shared" (throw "ignored")
2124 => "--without-shared"
2129 withFeatureAs = flag: feature: value:
2130 withFeature flag feature + optionalString flag "=${value}";
2133 Create a fixed width string with additional prefix to match
2136 This function will fail if the input string is longer than the
2143 : 1\. Function argument
2146 : 2\. Function argument
2149 : 3\. Function argument
2154 fixedWidthString :: int -> string -> string -> string
2159 ## `lib.strings.fixedWidthString` usage example
2162 fixedWidthString 5 "0" (toString 15)
2168 fixedWidthString = width: filler: str:
2170 strw = lib.stringLength str;
2171 reqWidth = width - (lib.stringLength filler);
2173 assert lib.assertMsg (strw <= width)
2174 "fixedWidthString: requested string length (${
2175 toString width}) must not be shorter than actual length (${
2177 if strw == width then str else filler + fixedWidthString reqWidth filler str;
2180 Format a number adding leading zeroes up to fixed width.
2186 : 1\. Function argument
2189 : 2\. Function argument
2194 fixedWidthNumber :: int -> int -> string
2199 ## `lib.strings.fixedWidthNumber` usage example
2202 fixedWidthNumber 5 15
2208 fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
2211 Convert a float to a string, but emit a warning when precision is lost
2212 during the conversion
2218 : 1\. Function argument
2224 floatToString :: float -> string
2229 ## `lib.strings.floatToString` usage example
2232 floatToString 0.000001
2234 floatToString 0.0000001
2235 => trace: warning: Imprecise conversion from float to string 0.000000
2241 floatToString = float: let
2242 result = toString float;
2243 precise = float == fromJSON result;
2244 in lib.warnIf (!precise) "Imprecise conversion from float to string ${result}"
2248 Check whether a value `val` can be coerced to a string.
2251 Soft-deprecated function. While the original implementation is available as
2252 `isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
2258 : 1\. Function argument
2263 isCoercibleToString :: a -> bool
2266 isCoercibleToString = lib.warnIf (lib.isInOldestRelease 2305)
2267 "lib.strings.isCoercibleToString is deprecated in favor of either isStringLike or isConvertibleWithToString. Only use the latter if it needs to return true for null, numbers, booleans and list of similarly coercibles."
2268 isConvertibleWithToString;
2271 Check whether a list or other value `x` can be passed to toString.
2273 Many types of value are coercible to string this way, including `int`, `float`,
2274 `null`, `bool`, `list` of similarly coercible values.
2279 : 1\. Function argument
2284 isConvertibleWithToString :: a -> bool
2287 isConvertibleWithToString = let
2288 types = [ "null" "int" "float" "bool" ];
2291 elem (typeOf x) types ||
2292 (isList x && lib.all isConvertibleWithToString x);
2295 Check whether a value can be coerced to a string.
2296 The value must be a string, path, or attribute set.
2298 String-like values can be used without explicit conversion in
2299 string interpolations and in most functions that expect a string.
2305 : 1\. Function argument
2310 isStringLike :: a -> bool
2320 Check whether a value `x` is a store path.
2326 : 1\. Function argument
2331 isStorePath :: a -> bool
2336 ## `lib.strings.isStorePath` usage example
2339 isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
2341 isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"
2343 isStorePath pkgs.python
2345 isStorePath [] || isStorePath 42 || isStorePath {} || …
2352 if isStringLike x then
2353 let str = toString x; in
2354 substring 0 1 str == "/"
2355 && dirOf str == storeDir
2360 Parse a string as an int. Does not support parsing of integers with preceding zero due to
2361 ambiguity between zero-padded and octal numbers. See toIntBase10.
2366 : A string to be interpreted as an int.
2371 toInt :: string -> int
2376 ## `lib.strings.toInt` usage example
2389 => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
2392 => error: floating point JSON numbers are not supported
2399 matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*";
2400 matchLeadingZero = match "0[[:digit:]]+";
2404 # RegEx: Match any leading whitespace, possibly a '-', one or more digits,
2405 # and finally match any trailing whitespace.
2406 strippedInput = matchStripInput str;
2408 # RegEx: Match a leading '0' then one or more digits.
2409 isLeadingZero = matchLeadingZero (head strippedInput) == [];
2411 # Attempt to parse input
2412 parsedInput = fromJSON (head strippedInput);
2414 generalError = "toInt: Could not convert ${escapeNixString str} to int.";
2417 # Error on presence of non digit characters.
2418 if strippedInput == null
2419 then throw generalError
2420 # Error on presence of leading zero/octal ambiguity.
2421 else if isLeadingZero
2422 then throw "toInt: Ambiguity in interpretation of ${escapeNixString str} between octal and zero padded integer."
2423 # Error if parse function fails.
2424 else if !isInt parsedInput
2425 then throw generalError
2431 Parse a string as a base 10 int. This supports parsing of zero-padded integers.
2436 : A string to be interpreted as an int.
2441 toIntBase10 :: string -> int
2446 ## `lib.strings.toIntBase10` usage example
2462 => error: floating point JSON numbers are not supported
2469 matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*";
2470 matchZero = match "0+";
2474 # RegEx: Match any leading whitespace, then match any zero padding,
2475 # capture possibly a '-' followed by one or more digits,
2476 # and finally match any trailing whitespace.
2477 strippedInput = matchStripInput str;
2479 # RegEx: Match at least one '0'.
2480 isZero = matchZero (head strippedInput) == [];
2482 # Attempt to parse input
2483 parsedInput = fromJSON (head strippedInput);
2485 generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";
2488 # Error on presence of non digit characters.
2489 if strippedInput == null
2490 then throw generalError
2491 # In the special case zero-padded zero (00000), return early.
2494 # Error if parse function fails.
2495 else if !isInt parsedInput
2496 then throw generalError
2501 Read a list of paths from `file`, relative to the `rootPath`.
2502 Lines beginning with `#` are treated as comments and ignored.
2503 Whitespace is significant.
2506 This function is deprecated and should be avoided.
2510 This function is not performant and should be avoided.
2516 : 1\. Function argument
2519 : 2\. Function argument
2524 readPathsFromFile :: string -> string -> [string]
2529 ## `lib.strings.readPathsFromFile` usage example
2532 readPathsFromFile /prefix
2533 ./pkgs/development/libraries/qt-5/5.4/qtbase/series
2534 => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
2535 "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
2536 "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
2537 "/prefix/nix-profiles-library-paths.patch"
2538 "/prefix/compose-search-path.patch" ]
2543 readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead."
2546 lines = lib.splitString "\n" (readFile file);
2547 removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line));
2548 relativePaths = removeComments lines;
2549 absolutePaths = map (path: rootPath + "/${path}") relativePaths;
2554 Read the contents of a file removing the trailing \n
2560 : 1\. Function argument
2565 fileContents :: path -> string
2570 ## `lib.strings.fileContents` usage example
2573 $ echo "1.0" > ./version
2575 fileContents ./version
2581 fileContents = file: removeSuffix "\n" (readFile file);
2585 Creates a valid derivation name from a potentially invalid one.
2590 : 1\. Function argument
2595 sanitizeDerivationName :: String -> String
2600 ## `lib.strings.sanitizeDerivationName` usage example
2603 sanitizeDerivationName "../hello.bar # foo"
2605 sanitizeDerivationName ""
2607 sanitizeDerivationName pkgs.hello
2608 => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10"
2613 sanitizeDerivationName =
2614 let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*";
2617 # First detect the common case of already valid strings, to speed those up
2618 if stringLength string <= 207 && okRegex string != null
2619 then unsafeDiscardStringContext string
2620 else lib.pipe string [
2621 # Get rid of string context. This is safe under the assumption that the
2622 # resulting string is only used as a derivation name
2623 unsafeDiscardStringContext
2624 # Strip all leading "."
2625 (x: elemAt (match "\\.*(.*)" x) 0)
2626 # Split out all invalid characters
2627 # https://github.com/NixOS/nix/blob/2.3.2/src/libstore/store-api.cc#L85-L112
2628 # https://github.com/NixOS/nix/blob/2242be83c61788b9c0736a92bb0b5c7bbfc40803/nix-rust/src/store/path.rs#L100-L125
2629 (split "[^[:alnum:]+._?=-]+")
2630 # Replace invalid character ranges with a "-"
2631 (concatMapStrings (s: if lib.isList s then "-" else s))
2632 # Limit to 211 characters (minus 4 chars for ".drv")
2633 (x: substring (lib.max (stringLength x - 207) 0) (-1) x)
2634 # If the result is empty, replace it with "unknown"
2635 (x: if stringLength x == 0 then "unknown" else x)
2639 Computes the Levenshtein distance between two strings `a` and `b`.
2641 Complexity O(n*m) where n and m are the lengths of the strings.
2642 Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742
2648 : 1\. Function argument
2651 : 2\. Function argument
2656 levenshtein :: string -> string -> int
2661 ## `lib.strings.levenshtein` usage example
2664 levenshtein "foo" "foo"
2666 levenshtein "book" "hook"
2668 levenshtein "hello" "Heyo"
2674 levenshtein = a: b: let
2675 # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1)
2676 arr = lib.genList (i:
2679 ) (stringLength b + 1)
2680 ) (stringLength a + 1);
2681 d = x: y: lib.elemAt (lib.elemAt arr x) y;
2683 let c = if substring (i - 1) 1 a == substring (j - 1) 1 b
2687 else if i == 0 then j
2689 ( lib.min (d (i - 1) j + 1) (d i (j - 1) + 1))
2690 ( d (i - 1) (j - 1) + c );
2691 in d (stringLength a) (stringLength b);
2694 Returns the length of the prefix that appears in both strings `a` and `b`.
2700 : 1\. Function argument
2703 : 2\. Function argument
2708 commonPrefixLength :: string -> string -> int
2711 commonPrefixLength = a: b:
2713 m = lib.min (stringLength a) (stringLength b);
2714 go = i: if i >= m then m else if substring i 1 a == substring i 1 b then go (i + 1) else i;
2718 Returns the length of the suffix common to both strings `a` and `b`.
2724 : 1\. Function argument
2727 : 2\. Function argument
2732 commonSuffixLength :: string -> string -> int
2735 commonSuffixLength = a: b:
2737 m = lib.min (stringLength a) (stringLength b);
2738 go = i: if i >= m then m else if substring (stringLength a - i - 1) 1 a == substring (stringLength b - i - 1) 1 b then go (i + 1) else i;
2742 Returns whether the levenshtein distance between two strings `a` and `b` is at most some value `k`.
2744 Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise
2749 : Distance threshold
2760 levenshteinAtMost :: int -> string -> string -> bool
2765 ## `lib.strings.levenshteinAtMost` usage example
2768 levenshteinAtMost 0 "foo" "foo"
2770 levenshteinAtMost 1 "foo" "boa"
2772 levenshteinAtMost 2 "foo" "boa"
2774 levenshteinAtMost 2 "This is a sentence" "this is a sentense."
2776 levenshteinAtMost 3 "This is a sentence" "this is a sentense."
2782 levenshteinAtMost = let
2783 infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1;
2785 # This function takes two strings stripped by their common pre and suffix,
2786 # and returns whether they differ by at most two by Levenshtein distance.
2787 # Because of this stripping, if they do indeed differ by at most two edits,
2788 # we know that those edits were (if at all) done at the start or the end,
2789 # while the middle has to have stayed the same. This fact is used in the
2791 infixDifferAtMost2 = x: y:
2793 xlen = stringLength x;
2794 ylen = stringLength y;
2795 # This function is only called with |x| >= |y| and |x| - |y| <= 2, so
2796 # diff is one of 0, 1 or 2
2799 # Infix of x and y, stripped by the left and right most character
2800 xinfix = substring 1 (xlen - 2) x;
2801 yinfix = substring 1 (ylen - 2) y;
2803 # x and y but a character deleted at the left or right
2804 xdelr = substring 0 (xlen - 1) x;
2805 xdell = substring 1 (xlen - 1) x;
2806 ydelr = substring 0 (ylen - 1) y;
2807 ydell = substring 1 (ylen - 1) y;
2809 # A length difference of 2 can only be gotten with 2 delete edits,
2810 # which have to have happened at the start and end of x
2811 # Example: "abcdef" -> "bcde"
2812 if diff == 2 then xinfix == y
2813 # A length difference of 1 can only be gotten with a deletion on the
2814 # right and a replacement on the left or vice versa.
2815 # Example: "abcdef" -> "bcdez" or "zbcde"
2816 else if diff == 1 then xinfix == ydelr || xinfix == ydell
2817 # No length difference can either happen through replacements on both
2818 # sides, or a deletion on the left and an insertion on the right or
2820 # Example: "abcdef" -> "zbcdez" or "bcdefz" or "zabcde"
2821 else xinfix == yinfix || xdelr == ydell || xdell == ydelr;
2823 in k: if k <= 0 then a: b: a == b else
2826 alen = stringLength a;
2827 blen = stringLength b;
2828 prelen = commonPrefixLength a b;
2829 suflen = commonSuffixLength a b;
2830 presuflen = prelen + suflen;
2831 ainfix = substring prelen (alen - presuflen) a;
2832 binfix = substring prelen (blen - presuflen) b;
2834 # Make a be the bigger string
2835 if alen < blen then f b a
2836 # If a has over k more characters than b, even with k deletes on a, b can't be reached
2837 else if alen - blen > k then false
2838 else if k == 1 then infixDifferAtMost1 ainfix binfix
2839 else if k == 2 then infixDifferAtMost2 ainfix binfix
2840 else levenshtein ainfix binfix <= k;