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! "}
412 # Define our own whitespace character class instead of using
413 # `[:space:]`, which is not well-defined.
416 # To match up until trailing whitespace, we need to capture a
417 # group that ends with a non-whitespace character.
420 "[${chars}]*(.*[^${chars}])[${chars}]*"
424 "(.*[^${chars}])[${chars}]*"
430 # If the string was empty or entirely whitespace,
431 # then the regex may not match and `res` will be `null`.
434 optionalString (res != null) (head res);
437 Construct a Unix-style, colon-separated search path consisting of
438 the given `subDir` appended to each of the given paths.
443 : Directory name to append
451 makeSearchPath :: string -> [string] -> string
456 ## `lib.strings.makeSearchPath` usage example
459 makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
460 => "/root/bin:/usr/bin:/usr/local/bin"
461 makeSearchPath "bin" [""]
470 concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths));
473 Construct a Unix-style search path by appending the given
474 `subDir` to the specified `output` of each of the packages.
476 If no output by the given name is found, fallback to `.out` and then to
483 : Package output to use
486 : Directory name to append
494 makeSearchPathOutput :: string -> string -> [package] -> string
499 ## `lib.strings.makeSearchPathOutput` usage example
502 makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ]
503 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin"
508 makeSearchPathOutput =
511 pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs);
514 Construct a library search path (such as RPATH) containing the
515 libraries for a set of packages
525 makeLibraryPath :: [package] -> string
530 ## `lib.strings.makeLibraryPath` usage example
533 makeLibraryPath [ "/usr" "/usr/local" ]
534 => "/usr/lib:/usr/local/lib"
535 pkgs = import <nixpkgs> { }
536 makeLibraryPath [ pkgs.openssl pkgs.zlib ]
537 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib"
542 makeLibraryPath = makeSearchPathOutput "lib" "lib";
545 Construct an include search path (such as C_INCLUDE_PATH) containing the
546 header files for a set of packages or paths.
556 makeIncludePath :: [package] -> string
561 ## `lib.strings.makeIncludePath` usage example
564 makeIncludePath [ "/usr" "/usr/local" ]
565 => "/usr/include:/usr/local/include"
566 pkgs = import <nixpkgs> { }
567 makeIncludePath [ pkgs.openssl pkgs.zlib ]
568 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/include:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8-dev/include"
573 makeIncludePath = makeSearchPathOutput "dev" "include";
576 Construct a binary search path (such as $PATH) containing the
577 binaries for a set of packages.
587 makeBinPath :: [package] -> string
592 ## `lib.strings.makeBinPath` usage example
595 makeBinPath ["/root" "/usr" "/usr/local"]
596 => "/root/bin:/usr/bin:/usr/local/bin"
601 makeBinPath = makeSearchPathOutput "bin" "bin";
604 Normalize path, removing extraneous /s
610 : 1\. Function argument
615 normalizePath :: string -> string
620 ## `lib.strings.normalizePath` usage example
623 normalizePath "/a//b///c/"
633 lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported.
634 Path values are always normalised in Nix, so there's no need to call this function on them.
635 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.
636 This behavior is deprecated and will throw an error in the future.''
639 (x: y: if y == "/" && hasSuffix "/" x then x else x+y)
641 (stringToCharacters s)
645 Depending on the boolean `cond', return either the given string
646 or the empty string. Useful to concatenate against a bigger string.
655 : String to return if condition is true
660 optionalString :: bool -> string -> string
665 ## `lib.strings.optionalString` usage example
668 optionalString true "some-string"
670 optionalString false "some-string"
678 string: if cond then string else "";
681 Determine whether a string has given prefix.
687 : Prefix to check for
695 hasPrefix :: string -> string -> bool
700 ## `lib.strings.hasPrefix` usage example
703 hasPrefix "foo" "foobar"
705 hasPrefix "foo" "barfoo"
714 # Before 23.05, paths would be copied to the store before converting them
715 # to strings and comparing. This was surprising and confusing.
719 lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported.
720 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
721 This function also copies the path to the Nix store, which may not be what you want.
722 This behavior is deprecated and will throw an error in the future.
723 You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.''
724 (substring 0 (stringLength pref) str == pref);
727 Determine whether a string has given suffix.
733 : Suffix to check for
741 hasSuffix :: string -> string -> bool
746 ## `lib.strings.hasSuffix` usage example
749 hasSuffix "foo" "foobar"
751 hasSuffix "foo" "barfoo"
761 lenContent = stringLength content;
762 lenSuffix = stringLength suffix;
764 # Before 23.05, paths would be copied to the store before converting them
765 # to strings and comparing. This was surprising and confusing.
769 lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
770 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
771 This function also copies the path to the Nix store, which may not be what you want.
772 This behavior is deprecated and will throw an error in the future.''
774 lenContent >= lenSuffix
775 && substring (lenContent - lenSuffix) lenContent content == suffix
779 Determine whether a string contains the given infix
785 : 1\. Function argument
788 : 2\. Function argument
793 hasInfix :: string -> string -> bool
798 ## `lib.strings.hasInfix` usage example
807 hasInfix "foo" "abcd"
813 hasInfix = infix: content:
814 # Before 23.05, paths would be copied to the store before converting them
815 # to strings and comparing. This was surprising and confusing.
819 lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported.
820 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
821 This function also copies the path to the Nix store, which may not be what you want.
822 This behavior is deprecated and will throw an error in the future.''
823 (builtins.match ".*${escapeRegex infix}.*" "${content}" != null);
826 Convert a string `s` to a list of characters (i.e. singleton strings).
827 This allows you to, e.g., map a function over each character. However,
828 note that this will likely be horribly inefficient; Nix is not a
829 general purpose programming language. Complex string manipulations
830 should, if appropriate, be done in a derivation.
831 Also note that Nix treats strings as a list of bytes and thus doesn't
838 : 1\. Function argument
843 stringToCharacters :: string -> [string]
848 ## `lib.strings.stringToCharacters` usage example
851 stringToCharacters ""
853 stringToCharacters "abc"
855 stringToCharacters "🦄"
856 => [ "�" "�" "�" "�" ]
861 stringToCharacters = s:
862 genList (p: substring p 1 s) (stringLength s);
865 Manipulate a string character by character and replace them by
866 strings before concatenating the results.
872 : Function to map over each individual character
880 stringAsChars :: (string -> string) -> string -> string
885 ## `lib.strings.stringAsChars` usage example
888 stringAsChars (x: if x == "a" then "i" else x) "nax"
895 # Function to map over each individual character
899 map f (stringToCharacters s)
903 Convert char to ascii value, must be in printable range
909 : 1\. Function argument
914 charToInt :: string -> int
919 ## `lib.strings.charToInt` usage example
930 charToInt = c: builtins.getAttr c asciiTable;
933 Escape occurrence of the elements of `list` in `string` by
934 prefixing it with a backslash.
940 : 1\. Function argument
943 : 2\. Function argument
948 escape :: [string] -> string -> string
953 ## `lib.strings.escape` usage example
956 escape ["(" ")"] "(foo)"
962 escape = list: replaceStrings list (map (c: "\\${c}") list);
965 Escape occurrence of the element of `list` in `string` by
966 converting to its ASCII value and prefixing it with \\x.
967 Only works for printable ascii characters.
973 : 1\. Function argument
976 : 2\. Function argument
981 escapeC = [string] -> string -> string
986 ## `lib.strings.escapeC` usage example
989 escapeC [" "] "foo bar"
995 escapeC = list: replaceStrings list (map (c: "\\x${ toLower (lib.toHexString (charToInt c))}") list);
998 Escape the `string` so it can be safely placed inside a URL
1004 : 1\. Function argument
1009 escapeURL :: string -> string
1014 ## `lib.strings.escapeURL` usage example
1017 escapeURL "foo/bar baz"
1018 => "foo%2Fbar%20baz"
1024 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" "-" "_" "." "~" ];
1025 toEscape = builtins.removeAttrs asciiTable unreserved;
1027 replaceStrings (builtins.attrNames toEscape) (lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") toEscape);
1030 Quote `string` to be used safely within the Bourne shell if it has any
1037 : 1\. Function argument
1042 escapeShellArg :: string -> string
1047 ## `lib.strings.escapeShellArg` usage example
1050 escapeShellArg "esc'ape\nme"
1051 => "'esc'\\''ape\nme'"
1056 escapeShellArg = arg:
1058 string = toString arg;
1060 if match "[[:alnum:],._+:@%/-]+" string == null
1061 then "'${replaceStrings ["'"] ["'\\''"] string}'"
1065 Quote all arguments that have special characters to be safely passed to the
1071 : 1\. Function argument
1076 escapeShellArgs :: [string] -> string
1081 ## `lib.strings.escapeShellArgs` usage example
1084 escapeShellArgs ["one" "two three" "four'five"]
1085 => "one 'two three' 'four'\\''five'"
1090 escapeShellArgs = concatMapStringsSep " " escapeShellArg;
1093 Test whether the given `name` is a valid POSIX shell variable name.
1099 : 1\. Function argument
1109 ## `lib.strings.isValidPosixName` usage example
1112 isValidPosixName "foo_bar000"
1114 isValidPosixName "0-bad.jpg"
1120 isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
1123 Translate a Nix value into a shell variable declaration, with proper escaping.
1125 The value can be a string (mapped to a regular variable), a list of strings
1126 (mapped to a Bash-style array) or an attribute set of strings (mapped to a
1127 Bash-style associative array). Note that "string" includes string-coercible
1128 values like paths or derivations.
1130 Strings are translated into POSIX sh-compatible code; lists and attribute sets
1131 assume a shell that understands Bash syntax (e.g. Bash or ZSH).
1137 : 1\. Function argument
1140 : 2\. Function argument
1145 string -> ( string | [string] | { ${name} :: string; } ) -> string
1150 ## `lib.strings.toShellVar` usage example
1154 ${toShellVar "foo" "some string"}
1155 [[ "$foo" == "some string" ]]
1161 toShellVar = name: value:
1162 lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
1163 if isAttrs value && ! isStringLike value then
1164 "declare -A ${name}=(${
1165 concatStringsSep " " (lib.mapAttrsToList (n: v:
1166 "[${escapeShellArg n}]=${escapeShellArg v}"
1169 else if isList value then
1170 "declare -a ${name}=(${escapeShellArgs value})"
1172 "${name}=${escapeShellArg value}"
1176 Translate an attribute set `vars` into corresponding shell variable declarations
1183 : 1\. Function argument
1189 ${name} :: string | [ string ] | { ${key} :: string; };
1195 ## `lib.strings.toShellVars` usage example
1202 ${toShellVars { inherit foo bar; }}
1203 [[ "$foo" == "$bar" ]]
1209 toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
1212 Turn a string `s` into a Nix expression representing that string
1217 : 1\. Function argument
1222 escapeNixString :: string -> string
1227 ## `lib.strings.escapeNixString` usage example
1230 escapeNixString "hello\${}\n"
1231 => "\"hello\\\${}\\n\""
1236 escapeNixString = s: escape ["$"] (toJSON s);
1239 Turn a string `s` into an exact regular expression
1244 : 1\. Function argument
1249 escapeRegex :: string -> string
1254 ## `lib.strings.escapeRegex` usage example
1257 escapeRegex "[^a-z]*"
1263 escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
1266 Quotes a string `s` if it can't be used as an identifier directly.
1272 : 1\. Function argument
1277 escapeNixIdentifier :: string -> string
1282 ## `lib.strings.escapeNixIdentifier` usage example
1285 escapeNixIdentifier "hello"
1287 escapeNixIdentifier "0abc"
1293 escapeNixIdentifier = s:
1294 # Regex from https://github.com/NixOS/nix/blob/d048577909e383439c2549e849c5c2f2016c997e/src/libexpr/lexer.l#L91
1295 if match "[a-zA-Z_][a-zA-Z0-9_'-]*" s != null
1296 then s else escapeNixString s;
1299 Escapes a string `s` such that it is safe to include verbatim in an XML
1305 : 1\. Function argument
1310 escapeXML :: string -> string
1315 ## `lib.strings.escapeXML` usage example
1318 escapeXML ''"test" 'test' < & >''
1319 => ""test" 'test' < & >"
1324 escapeXML = builtins.replaceStrings
1325 ["\"" "'" "<" ">" "&"]
1326 [""" "'" "<" ">" "&"];
1328 # warning added 12-12-2022
1329 replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
1331 # Case conversion utilities.
1332 lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
1333 upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1336 Converts an ASCII string `s` to lower-case.
1341 : The string to convert to lower-case.
1346 toLower :: string -> string
1351 ## `lib.strings.toLower` usage example
1360 toLower = replaceStrings upperChars lowerChars;
1363 Converts an ASCII string `s` to upper-case.
1368 : The string to convert to upper-case.
1374 toUpper :: string -> string
1379 ## `lib.strings.toUpper` usage example
1388 toUpper = replaceStrings lowerChars upperChars;
1391 Appends string context from string like object `src` to `target`.
1394 This is an implementation
1395 detail of Nix and should be used carefully.
1398 Strings in Nix carry an invisible `context` which is a list of strings
1399 representing store paths. If the string is later used in a derivation
1400 attribute, the derivation will properly populate the inputDrvs and
1407 : The string to take the context from. If the argument is not a string,
1408 it will be implicitly converted to a string.
1411 : The string to append the context to. If the argument is not a string,
1412 it will be implicitly converted to a string.
1417 addContextFrom :: string -> string -> string
1422 ## `lib.strings.addContextFrom` usage example
1425 pkgs = import <nixpkgs> { };
1426 addContextFrom pkgs.coreutils "bar"
1430 The context can be displayed using the `toString` function:
1433 nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
1435 "/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
1441 addContextFrom = src: target: substring 0 0 src + target;
1444 Cut a string with a separator and produces a list of strings which
1445 were separated by this separator.
1450 : 1\. Function argument
1453 : 2\. Function argument
1458 splitString :: string -> string -> [string]
1463 ## `lib.strings.splitString` usage example
1466 splitString "." "foo.bar.baz"
1467 => [ "foo" "bar" "baz" ]
1468 splitString "/" "/usr/local/bin"
1469 => [ "" "usr" "local" "bin" ]
1474 splitString = sep: s:
1476 splits = builtins.filter builtins.isString (builtins.split (escapeRegex (toString sep)) (toString s));
1478 map (addContextFrom s) splits;
1482 Return a string without the specified prefix, if the prefix matches.
1487 : Prefix to remove if it matches
1495 removePrefix :: string -> string -> string
1500 ## `lib.strings.removePrefix` usage example
1503 removePrefix "foo." "foo.bar.baz"
1505 removePrefix "xxx" "foo.bar.baz"
1514 # Before 23.05, paths would be copied to the store before converting them
1515 # to strings and comparing. This was surprising and confusing.
1519 lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported.
1520 There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case.
1521 This function also copies the path to the Nix store, which may not be what you want.
1522 This behavior is deprecated and will throw an error in the future.''
1524 preLen = stringLength prefix;
1526 if substring 0 preLen str == prefix then
1527 # -1 will take the string until the end
1528 substring preLen (-1) str
1533 Return a string without the specified suffix, if the suffix matches.
1539 : Suffix to remove if it matches
1547 removeSuffix :: string -> string -> string
1552 ## `lib.strings.removeSuffix` usage example
1555 removeSuffix "front" "homefront"
1557 removeSuffix "xxx" "homefront"
1566 # Before 23.05, paths would be copied to the store before converting them
1567 # to strings and comparing. This was surprising and confusing.
1571 lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
1572 There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case.
1573 This function also copies the path to the Nix store, which may not be what you want.
1574 This behavior is deprecated and will throw an error in the future.''
1576 sufLen = stringLength suffix;
1577 sLen = stringLength str;
1579 if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then
1580 substring 0 (sLen - sufLen) str
1585 Return true if string `v1` denotes a version older than `v2`.
1591 : 1\. Function argument
1594 : 2\. Function argument
1599 versionOlder :: String -> String -> Bool
1604 ## `lib.strings.versionOlder` usage example
1607 versionOlder "1.1" "1.2"
1609 versionOlder "1.1" "1.1"
1615 versionOlder = v1: v2: compareVersions v2 v1 == 1;
1618 Return true if string v1 denotes a version equal to or newer than v2.
1624 : 1\. Function argument
1627 : 2\. Function argument
1632 versionAtLeast :: String -> String -> Bool
1637 ## `lib.strings.versionAtLeast` usage example
1640 versionAtLeast "1.1" "1.0"
1642 versionAtLeast "1.1" "1.1"
1644 versionAtLeast "1.1" "1.2"
1650 versionAtLeast = v1: v2: !versionOlder v1 v2;
1653 This function takes an argument `x` that's either a derivation or a
1654 derivation's "name" attribute and extracts the name part from that
1660 : 1\. Function argument
1665 getName :: String | Derivation -> String
1671 ## `lib.strings.getName` usage example
1674 getName "youtube-dl-2016.01.01"
1676 getName pkgs.youtube-dl
1683 parse = drv: (parseDrvName drv).name;
1687 else x.pname or (parse x.name);
1690 This function takes an argument `x` that's either a derivation or a
1691 derivation's "name" attribute and extracts the version part from that
1698 : 1\. Function argument
1703 getVersion :: String | Derivation -> String
1708 ## `lib.strings.getVersion` usage example
1711 getVersion "youtube-dl-2016.01.01"
1713 getVersion pkgs.youtube-dl
1720 parse = drv: (parseDrvName drv).version;
1724 else x.version or (parse x.name);
1727 Extract name and version from a URL as shown in the examples.
1729 Separator `sep` is used to determine the end of the extension.
1735 : 1\. Function argument
1738 : 2\. Function argument
1743 nameFromURL :: String -> String
1748 ## `lib.strings.nameFromURL` usage example
1751 nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
1753 nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
1759 nameFromURL = url: sep:
1761 components = splitString "/" url;
1762 filename = lib.last components;
1763 name = head (splitString sep filename);
1764 in assert name != filename; name;
1767 Create a `"-D<feature>:<type>=<value>"` string that can be passed to typical
1773 : The feature to be set
1776 : The type of the feature to be set, as described in
1777 https://cmake.org/cmake/help/latest/command/set.html
1778 the possible values (case insensitive) are:
1779 BOOL FILEPATH PATH STRING INTERNAL
1787 cmakeOptionType :: string -> string -> string -> string
1792 ## `lib.strings.cmakeOptionType` usage example
1795 cmakeOptionType "string" "ENGINE" "sdl2"
1796 => "-DENGINE:STRING=sdl2"
1801 cmakeOptionType = let
1802 types = [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ];
1803 in type: feature: value:
1804 assert (elem (toUpper type) types);
1805 assert (isString feature);
1806 assert (isString value);
1807 "-D${feature}:${toUpper type}=${value}";
1810 Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
1817 : The condition to be made true or false
1820 : The controlling flag of the condition
1825 cmakeBool :: string -> bool -> string
1830 ## `lib.strings.cmakeBool` usage example
1833 cmakeBool "ENABLE_STATIC_LIBS" false
1834 => "-DENABLESTATIC_LIBS:BOOL=FALSE"
1839 cmakeBool = condition: flag:
1840 assert (lib.isString condition);
1841 assert (lib.isBool flag);
1842 cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag));
1845 Create a -D<feature>:STRING=<value> string that can be passed to typical
1847 This is the most typical usage, so it deserves a special case.
1853 : The feature to be set
1862 cmakeFeature :: string -> string -> string
1867 ## `lib.strings.cmakeFeature` usage example
1870 cmakeFeature "MODULES" "badblock"
1871 => "-DMODULES:STRING=badblock"
1876 cmakeFeature = feature: value:
1877 assert (lib.isString feature);
1878 assert (lib.isString value);
1879 cmakeOptionType "string" feature value;
1882 Create a -D<feature>=<value> string that can be passed to typical Meson
1889 : The feature to be set
1897 mesonOption :: string -> string -> string
1902 ## `lib.strings.mesonOption` usage example
1905 mesonOption "engine" "opengl"
1906 => "-Dengine=opengl"
1911 mesonOption = feature: value:
1912 assert (lib.isString feature);
1913 assert (lib.isString value);
1914 "-D${feature}=${value}";
1917 Create a -D<condition>={true,false} string that can be passed to typical
1924 : The condition to be made true or false
1927 : The controlling flag of the condition
1932 mesonBool :: string -> bool -> string
1937 ## `lib.strings.mesonBool` usage example
1940 mesonBool "hardened" true
1941 => "-Dhardened=true"
1942 mesonBool "static" false
1948 mesonBool = condition: flag:
1949 assert (lib.isString condition);
1950 assert (lib.isBool flag);
1951 mesonOption condition (lib.boolToString flag);
1954 Create a -D<feature>={enabled,disabled} string that can be passed to
1955 typical Meson invocations.
1961 : The feature to be enabled or disabled
1964 : The controlling flag
1969 mesonEnable :: string -> bool -> string
1974 ## `lib.strings.mesonEnable` usage example
1977 mesonEnable "docs" true
1979 mesonEnable "savage" false
1980 => "-Dsavage=disabled"
1985 mesonEnable = feature: flag:
1986 assert (lib.isString feature);
1987 assert (lib.isBool flag);
1988 mesonOption feature (if flag then "enabled" else "disabled");
1991 Create an --{enable,disable}-<feature> string that can be passed to
1992 standard GNU Autoconf scripts.
1998 : 1\. Function argument
2001 : 2\. Function argument
2006 enableFeature :: bool -> string -> string
2011 ## `lib.strings.enableFeature` usage example
2014 enableFeature true "shared"
2015 => "--enable-shared"
2016 enableFeature false "shared"
2017 => "--disable-shared"
2022 enableFeature = flag: feature:
2023 assert lib.isBool flag;
2024 assert lib.isString feature; # e.g. passing openssl instead of "openssl"
2025 "--${if flag then "enable" else "disable"}-${feature}";
2028 Create an --{enable-<feature>=<value>,disable-<feature>} string that can be passed to
2029 standard GNU Autoconf scripts.
2035 : 1\. Function argument
2038 : 2\. Function argument
2041 : 3\. Function argument
2046 enableFeatureAs :: bool -> string -> string -> string
2051 ## `lib.strings.enableFeatureAs` usage example
2054 enableFeatureAs true "shared" "foo"
2055 => "--enable-shared=foo"
2056 enableFeatureAs false "shared" (throw "ignored")
2057 => "--disable-shared"
2062 enableFeatureAs = flag: feature: value:
2063 enableFeature flag feature + optionalString flag "=${value}";
2066 Create an --{with,without}-<feature> string that can be passed to
2067 standard GNU Autoconf scripts.
2073 : 1\. Function argument
2076 : 2\. Function argument
2082 withFeature :: bool -> string -> string
2087 ## `lib.strings.withFeature` usage example
2090 withFeature true "shared"
2092 withFeature false "shared"
2093 => "--without-shared"
2098 withFeature = flag: feature:
2099 assert isString feature; # e.g. passing openssl instead of "openssl"
2100 "--${if flag then "with" else "without"}-${feature}";
2103 Create an --{with-<feature>=<value>,without-<feature>} string that can be passed to
2104 standard GNU Autoconf scripts.
2110 : 1\. Function argument
2113 : 2\. Function argument
2116 : 3\. Function argument
2121 withFeatureAs :: bool -> string -> string -> string
2127 ## `lib.strings.withFeatureAs` usage example
2130 withFeatureAs true "shared" "foo"
2131 => "--with-shared=foo"
2132 withFeatureAs false "shared" (throw "ignored")
2133 => "--without-shared"
2138 withFeatureAs = flag: feature: value:
2139 withFeature flag feature + optionalString flag "=${value}";
2142 Create a fixed width string with additional prefix to match
2145 This function will fail if the input string is longer than the
2152 : 1\. Function argument
2155 : 2\. Function argument
2158 : 3\. Function argument
2163 fixedWidthString :: int -> string -> string -> string
2168 ## `lib.strings.fixedWidthString` usage example
2171 fixedWidthString 5 "0" (toString 15)
2177 fixedWidthString = width: filler: str:
2179 strw = lib.stringLength str;
2180 reqWidth = width - (lib.stringLength filler);
2182 assert lib.assertMsg (strw <= width)
2183 "fixedWidthString: requested string length (${
2184 toString width}) must not be shorter than actual length (${
2186 if strw == width then str else filler + fixedWidthString reqWidth filler str;
2189 Format a number adding leading zeroes up to fixed width.
2195 : 1\. Function argument
2198 : 2\. Function argument
2203 fixedWidthNumber :: int -> int -> string
2208 ## `lib.strings.fixedWidthNumber` usage example
2211 fixedWidthNumber 5 15
2217 fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
2220 Convert a float to a string, but emit a warning when precision is lost
2221 during the conversion
2227 : 1\. Function argument
2233 floatToString :: float -> string
2238 ## `lib.strings.floatToString` usage example
2241 floatToString 0.000001
2243 floatToString 0.0000001
2244 => trace: warning: Imprecise conversion from float to string 0.000000
2250 floatToString = float: let
2251 result = toString float;
2252 precise = float == fromJSON result;
2253 in lib.warnIf (!precise) "Imprecise conversion from float to string ${result}"
2257 Check whether a value `val` can be coerced to a string.
2260 Soft-deprecated function. While the original implementation is available as
2261 `isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
2267 : 1\. Function argument
2272 isCoercibleToString :: a -> bool
2275 isCoercibleToString = lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2305)
2276 "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."
2277 isConvertibleWithToString;
2280 Check whether a list or other value `x` can be passed to toString.
2282 Many types of value are coercible to string this way, including `int`, `float`,
2283 `null`, `bool`, `list` of similarly coercible values.
2288 : 1\. Function argument
2293 isConvertibleWithToString :: a -> bool
2296 isConvertibleWithToString = let
2297 types = [ "null" "int" "float" "bool" ];
2300 elem (typeOf x) types ||
2301 (isList x && lib.all isConvertibleWithToString x);
2304 Check whether a value can be coerced to a string.
2305 The value must be a string, path, or attribute set.
2307 String-like values can be used without explicit conversion in
2308 string interpolations and in most functions that expect a string.
2314 : 1\. Function argument
2319 isStringLike :: a -> bool
2329 Check whether a value `x` is a store path.
2335 : 1\. Function argument
2340 isStorePath :: a -> bool
2345 ## `lib.strings.isStorePath` usage example
2348 isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
2350 isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"
2352 isStorePath pkgs.python
2354 isStorePath [] || isStorePath 42 || isStorePath {} || …
2361 if isStringLike x then
2362 let str = toString x; in
2363 substring 0 1 str == "/"
2364 && dirOf str == storeDir
2369 Parse a string as an int. Does not support parsing of integers with preceding zero due to
2370 ambiguity between zero-padded and octal numbers. See toIntBase10.
2375 : A string to be interpreted as an int.
2380 toInt :: string -> int
2385 ## `lib.strings.toInt` usage example
2398 => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
2401 => error: floating point JSON numbers are not supported
2408 matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*";
2409 matchLeadingZero = match "0[[:digit:]]+";
2413 # RegEx: Match any leading whitespace, possibly a '-', one or more digits,
2414 # and finally match any trailing whitespace.
2415 strippedInput = matchStripInput str;
2417 # RegEx: Match a leading '0' then one or more digits.
2418 isLeadingZero = matchLeadingZero (head strippedInput) == [];
2420 # Attempt to parse input
2421 parsedInput = fromJSON (head strippedInput);
2423 generalError = "toInt: Could not convert ${escapeNixString str} to int.";
2426 # Error on presence of non digit characters.
2427 if strippedInput == null
2428 then throw generalError
2429 # Error on presence of leading zero/octal ambiguity.
2430 else if isLeadingZero
2431 then throw "toInt: Ambiguity in interpretation of ${escapeNixString str} between octal and zero padded integer."
2432 # Error if parse function fails.
2433 else if !isInt parsedInput
2434 then throw generalError
2440 Parse a string as a base 10 int. This supports parsing of zero-padded integers.
2445 : A string to be interpreted as an int.
2450 toIntBase10 :: string -> int
2455 ## `lib.strings.toIntBase10` usage example
2471 => error: floating point JSON numbers are not supported
2478 matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*";
2479 matchZero = match "0+";
2483 # RegEx: Match any leading whitespace, then match any zero padding,
2484 # capture possibly a '-' followed by one or more digits,
2485 # and finally match any trailing whitespace.
2486 strippedInput = matchStripInput str;
2488 # RegEx: Match at least one '0'.
2489 isZero = matchZero (head strippedInput) == [];
2491 # Attempt to parse input
2492 parsedInput = fromJSON (head strippedInput);
2494 generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";
2497 # Error on presence of non digit characters.
2498 if strippedInput == null
2499 then throw generalError
2500 # In the special case zero-padded zero (00000), return early.
2503 # Error if parse function fails.
2504 else if !isInt parsedInput
2505 then throw generalError
2510 Read a list of paths from `file`, relative to the `rootPath`.
2511 Lines beginning with `#` are treated as comments and ignored.
2512 Whitespace is significant.
2515 This function is deprecated and should be avoided.
2519 This function is not performant and should be avoided.
2525 : 1\. Function argument
2528 : 2\. Function argument
2533 readPathsFromFile :: string -> string -> [string]
2538 ## `lib.strings.readPathsFromFile` usage example
2541 readPathsFromFile /prefix
2542 ./pkgs/development/libraries/qt-5/5.4/qtbase/series
2543 => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
2544 "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
2545 "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
2546 "/prefix/nix-profiles-library-paths.patch"
2547 "/prefix/compose-search-path.patch" ]
2552 readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead."
2555 lines = lib.splitString "\n" (readFile file);
2556 removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line));
2557 relativePaths = removeComments lines;
2558 absolutePaths = map (path: rootPath + "/${path}") relativePaths;
2563 Read the contents of a file removing the trailing \n
2569 : 1\. Function argument
2574 fileContents :: path -> string
2579 ## `lib.strings.fileContents` usage example
2582 $ echo "1.0" > ./version
2584 fileContents ./version
2590 fileContents = file: removeSuffix "\n" (readFile file);
2594 Creates a valid derivation name from a potentially invalid one.
2599 : 1\. Function argument
2604 sanitizeDerivationName :: String -> String
2609 ## `lib.strings.sanitizeDerivationName` usage example
2612 sanitizeDerivationName "../hello.bar # foo"
2614 sanitizeDerivationName ""
2616 sanitizeDerivationName pkgs.hello
2617 => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10"
2622 sanitizeDerivationName =
2623 let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*";
2626 # First detect the common case of already valid strings, to speed those up
2627 if stringLength string <= 207 && okRegex string != null
2628 then unsafeDiscardStringContext string
2629 else lib.pipe string [
2630 # Get rid of string context. This is safe under the assumption that the
2631 # resulting string is only used as a derivation name
2632 unsafeDiscardStringContext
2633 # Strip all leading "."
2634 (x: elemAt (match "\\.*(.*)" x) 0)
2635 # Split out all invalid characters
2636 # https://github.com/NixOS/nix/blob/2.3.2/src/libstore/store-api.cc#L85-L112
2637 # https://github.com/NixOS/nix/blob/2242be83c61788b9c0736a92bb0b5c7bbfc40803/nix-rust/src/store/path.rs#L100-L125
2638 (split "[^[:alnum:]+._?=-]+")
2639 # Replace invalid character ranges with a "-"
2640 (concatMapStrings (s: if lib.isList s then "-" else s))
2641 # Limit to 211 characters (minus 4 chars for ".drv")
2642 (x: substring (lib.max (stringLength x - 207) 0) (-1) x)
2643 # If the result is empty, replace it with "unknown"
2644 (x: if stringLength x == 0 then "unknown" else x)
2648 Computes the Levenshtein distance between two strings `a` and `b`.
2650 Complexity O(n*m) where n and m are the lengths of the strings.
2651 Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742
2657 : 1\. Function argument
2660 : 2\. Function argument
2665 levenshtein :: string -> string -> int
2670 ## `lib.strings.levenshtein` usage example
2673 levenshtein "foo" "foo"
2675 levenshtein "book" "hook"
2677 levenshtein "hello" "Heyo"
2683 levenshtein = a: b: let
2684 # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1)
2685 arr = lib.genList (i:
2688 ) (stringLength b + 1)
2689 ) (stringLength a + 1);
2690 d = x: y: lib.elemAt (lib.elemAt arr x) y;
2692 let c = if substring (i - 1) 1 a == substring (j - 1) 1 b
2696 else if i == 0 then j
2698 ( lib.min (d (i - 1) j + 1) (d i (j - 1) + 1))
2699 ( d (i - 1) (j - 1) + c );
2700 in d (stringLength a) (stringLength b);
2703 Returns the length of the prefix that appears in both strings `a` and `b`.
2709 : 1\. Function argument
2712 : 2\. Function argument
2717 commonPrefixLength :: string -> string -> int
2720 commonPrefixLength = a: b:
2722 m = lib.min (stringLength a) (stringLength b);
2723 go = i: if i >= m then m else if substring i 1 a == substring i 1 b then go (i + 1) else i;
2727 Returns the length of the suffix common to both strings `a` and `b`.
2733 : 1\. Function argument
2736 : 2\. Function argument
2741 commonSuffixLength :: string -> string -> int
2744 commonSuffixLength = a: b:
2746 m = lib.min (stringLength a) (stringLength b);
2747 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;
2751 Returns whether the levenshtein distance between two strings `a` and `b` is at most some value `k`.
2753 Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise
2758 : Distance threshold
2769 levenshteinAtMost :: int -> string -> string -> bool
2774 ## `lib.strings.levenshteinAtMost` usage example
2777 levenshteinAtMost 0 "foo" "foo"
2779 levenshteinAtMost 1 "foo" "boa"
2781 levenshteinAtMost 2 "foo" "boa"
2783 levenshteinAtMost 2 "This is a sentence" "this is a sentense."
2785 levenshteinAtMost 3 "This is a sentence" "this is a sentense."
2791 levenshteinAtMost = let
2792 infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1;
2794 # This function takes two strings stripped by their common pre and suffix,
2795 # and returns whether they differ by at most two by Levenshtein distance.
2796 # Because of this stripping, if they do indeed differ by at most two edits,
2797 # we know that those edits were (if at all) done at the start or the end,
2798 # while the middle has to have stayed the same. This fact is used in the
2800 infixDifferAtMost2 = x: y:
2802 xlen = stringLength x;
2803 ylen = stringLength y;
2804 # This function is only called with |x| >= |y| and |x| - |y| <= 2, so
2805 # diff is one of 0, 1 or 2
2808 # Infix of x and y, stripped by the left and right most character
2809 xinfix = substring 1 (xlen - 2) x;
2810 yinfix = substring 1 (ylen - 2) y;
2812 # x and y but a character deleted at the left or right
2813 xdelr = substring 0 (xlen - 1) x;
2814 xdell = substring 1 (xlen - 1) x;
2815 ydelr = substring 0 (ylen - 1) y;
2816 ydell = substring 1 (ylen - 1) y;
2818 # A length difference of 2 can only be gotten with 2 delete edits,
2819 # which have to have happened at the start and end of x
2820 # Example: "abcdef" -> "bcde"
2821 if diff == 2 then xinfix == y
2822 # A length difference of 1 can only be gotten with a deletion on the
2823 # right and a replacement on the left or vice versa.
2824 # Example: "abcdef" -> "bcdez" or "zbcde"
2825 else if diff == 1 then xinfix == ydelr || xinfix == ydell
2826 # No length difference can either happen through replacements on both
2827 # sides, or a deletion on the left and an insertion on the right or
2829 # Example: "abcdef" -> "zbcdez" or "bcdefz" or "zabcde"
2830 else xinfix == yinfix || xdelr == ydell || xdell == ydelr;
2832 in k: if k <= 0 then a: b: a == b else
2835 alen = stringLength a;
2836 blen = stringLength b;
2837 prelen = commonPrefixLength a b;
2838 suflen = commonSuffixLength a b;
2839 presuflen = prelen + suflen;
2840 ainfix = substring prelen (alen - presuflen) a;
2841 binfix = substring prelen (blen - presuflen) b;
2843 # Make a be the bigger string
2844 if alen < blen then f b a
2845 # If a has over k more characters than b, even with k deletes on a, b can't be reached
2846 else if alen - blen > k then false
2847 else if k == 1 then infixDifferAtMost1 ainfix binfix
2848 else if k == 2 then infixDifferAtMost2 ainfix binfix
2849 else levenshtein ainfix binfix <= k;