chromium,chromedriver: 128.0.6613.137 -> 129.0.6668.58 (#342721)
[NixPkgs.git] / lib / strings.nix
blobffde541c695838e1d846798a071e00110b054dc3
1 /**
2   String manipulation functions.
3 */
4 { lib }:
5 let
7   inherit (builtins) length;
9   inherit (lib.trivial) warnIf;
11 asciiTable = import ./ascii-table.nix;
15 rec {
17   inherit (builtins)
18     compareVersions
19     elem
20     elemAt
21     filter
22     fromJSON
23     genList
24     head
25     isInt
26     isList
27     isAttrs
28     isPath
29     isString
30     match
31     parseDrvName
32     readFile
33     replaceStrings
34     split
35     storeDir
36     stringLength
37     substring
38     tail
39     toJSON
40     typeOf
41     unsafeDiscardStringContext
42     ;
44   /**
45     Concatenate a list of strings.
47     # Type
49     ```
50     concatStrings :: [string] -> string
51     ```
53     # Examples
54     :::{.example}
55     ## `lib.strings.concatStrings` usage example
57     ```nix
58     concatStrings ["foo" "bar"]
59     => "foobar"
60     ```
62     :::
63   */
64   concatStrings = builtins.concatStringsSep "";
66   /**
67     Map a function over a list and concatenate the resulting strings.
70     # Inputs
72     `f`
73     : 1\. Function argument
75     `list`
76     : 2\. Function argument
78     # Type
80     ```
81     concatMapStrings :: (a -> string) -> [a] -> string
82     ```
84     # Examples
85     :::{.example}
86     ## `lib.strings.concatMapStrings` usage example
88     ```nix
89     concatMapStrings (x: "a" + x) ["foo" "bar"]
90     => "afooabar"
91     ```
93     :::
94   */
95   concatMapStrings = f: list: concatStrings (map f list);
97   /**
98     Like `concatMapStrings` except that the f functions also gets the
99     position as a parameter.
102     # Inputs
104     `f`
105     : 1\. Function argument
107     `list`
108     : 2\. Function argument
110     # Type
112     ```
113     concatImapStrings :: (int -> a -> string) -> [a] -> string
114     ```
116     # Examples
117     :::{.example}
118     ## `lib.strings.concatImapStrings` usage example
120     ```nix
121     concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"]
122     => "1-foo2-bar"
123     ```
125     :::
126   */
127   concatImapStrings = f: list: concatStrings (lib.imap1 f list);
129   /**
130     Place an element between each element of a list
133     # Inputs
135     `separator`
136     : Separator to add between elements
138     `list`
139     : Input list
141     # Type
143     ```
144     intersperse :: a -> [a] -> [a]
145     ```
147     # Examples
148     :::{.example}
149     ## `lib.strings.intersperse` usage example
151     ```nix
152     intersperse "/" ["usr" "local" "bin"]
153     => ["usr" "/" "local" "/" "bin"].
154     ```
156     :::
157   */
158   intersperse =
159     separator:
160     list:
161     if list == [] || length list == 1
162     then list
163     else tail (lib.concatMap (x: [separator x]) list);
165   /**
166     Concatenate a list of strings with a separator between each element
168     # Inputs
170     `sep`
171     : Separator to add between elements
173     `list`
174     : List of input strings
176     # Type
178     ```
179     concatStringsSep :: string -> [string] -> string
180     ```
182     # Examples
183     :::{.example}
184     ## `lib.strings.concatStringsSep` usage example
186     ```nix
187     concatStringsSep "/" ["usr" "local" "bin"]
188     => "usr/local/bin"
189     ```
191     :::
192   */
193   concatStringsSep = builtins.concatStringsSep;
195   /**
196     Maps a function over a list of strings and then concatenates the
197     result with the specified separator interspersed between
198     elements.
201     # Inputs
203     `sep`
204     : Separator to add between elements
206     `f`
207     : Function to map over the list
209     `list`
210     : List of input strings
212     # Type
214     ```
215     concatMapStringsSep :: string -> (a -> string) -> [a] -> string
216     ```
218     # Examples
219     :::{.example}
220     ## `lib.strings.concatMapStringsSep` usage example
222     ```nix
223     concatMapStringsSep "-" (x: toUpper x)  ["foo" "bar" "baz"]
224     => "FOO-BAR-BAZ"
225     ```
227     :::
228   */
229   concatMapStringsSep =
230     sep:
231     f:
232     list: concatStringsSep sep (map f list);
234   /**
235     Same as `concatMapStringsSep`, but the mapping function
236     additionally receives the position of its argument.
239     # Inputs
241     `sep`
242     : Separator to add between elements
244     `f`
245     : Function that receives elements and their positions
247     `list`
248     : List of input strings
250     # Type
252     ```
253     concatIMapStringsSep :: string -> (int -> a -> string) -> [a] -> string
254     ```
256     # Examples
257     :::{.example}
258     ## `lib.strings.concatImapStringsSep` usage example
260     ```nix
261     concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
262     => "6-3-2"
263     ```
265     :::
266   */
267   concatImapStringsSep =
268     sep:
269     f:
270     list: concatStringsSep sep (lib.imap1 f list);
272   /**
273     Concatenate a list of strings, adding a newline at the end of each one.
274     Defined as `concatMapStrings (s: s + "\n")`.
276     # Inputs
278     `list`
279     : List of strings. Any element that is not a string will be implicitly converted to a string.
281     # Type
283     ```
284     concatLines :: [string] -> string
285     ```
287     # Examples
288     :::{.example}
289     ## `lib.strings.concatLines` usage example
291     ```nix
292     concatLines [ "foo" "bar" ]
293     => "foo\nbar\n"
294     ```
296     :::
297   */
298   concatLines = concatMapStrings (s: s + "\n");
300   /**
301     Repeat a string `n` times,
302     and concatenate the parts into a new string.
305     # Inputs
307     `n`
308     : 1\. Function argument
310     `s`
311     : 2\. Function argument
313     # Type
315     ```
316     replicate :: int -> string -> string
317     ```
319     # Examples
320     :::{.example}
321     ## `lib.strings.replicate` usage example
323     ```nix
324     replicate 3 "v"
325     => "vvv"
326     replicate 5 "hello"
327     => "hellohellohellohellohello"
328     ```
330     :::
331   */
332   replicate = n: s: concatStrings (lib.lists.replicate n s);
334   /**
335     Remove leading and trailing whitespace from a string `s`.
337     Whitespace is defined as any of the following characters:
338       " ", "\t" "\r" "\n"
340     # Inputs
342     `s`
343     : The string to trim
345     # Type
347     ```
348     trim :: string -> string
349     ```
351     # Examples
352     :::{.example}
353     ## `lib.strings.trim` usage example
355     ```nix
356     trim "   hello, world!   "
357     => "hello, world!"
358     ```
360     :::
361   */
362   trim = trimWith {
363     start = true;
364     end = true;
365   };
367   /**
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:
373       " ", "\t" "\r" "\n"
375     # Inputs
377     `config` (Attribute set)
378     : `start`
379       : Whether to trim leading whitespace (`false` by default)
381     : `end`
382       : Whether to trim trailing whitespace (`false` by default)
384     `s`
385     : The string to trim
387     # Type
389     ```
390     trimWith :: { start :: Bool; end :: Bool } -> String -> String
391     ```
393     # Examples
394     :::{.example}
395     ## `lib.strings.trimWith` usage example
397     ```nix
398     trimWith { start = true; } "   hello, world!   "}
399     => "hello, world!   "
401     trimWith { end = true; } "   hello, world!   "}
402     => "   hello, world!"
403     ```
404     :::
405   */
406   trimWith =
407     {
408       start ? false,
409       end ? false,
410     }:
411     let
412       # Define our own whitespace character class instead of using
413       # `[:space:]`, which is not well-defined.
414       chars = " \t\r\n";
416       # To match up until trailing whitespace, we need to capture a
417       # group that ends with a non-whitespace character.
418       regex =
419         if start && end then
420           "[${chars}]*(.*[^${chars}])[${chars}]*"
421         else if start then
422           "[${chars}]*(.*)"
423         else if end then
424           "(.*[^${chars}])[${chars}]*"
425         else
426           "(.*)";
427     in
428     s:
429     let
430       # If the string was empty or entirely whitespace,
431       # then the regex may not match and `res` will be `null`.
432       res = match regex s;
433     in
434     optionalString (res != null) (head res);
436   /**
437     Construct a Unix-style, colon-separated search path consisting of
438     the given `subDir` appended to each of the given paths.
440     # Inputs
442     `subDir`
443     : Directory name to append
445     `paths`
446     : List of base paths
448     # Type
450     ```
451     makeSearchPath :: string -> [string] -> string
452     ```
454     # Examples
455     :::{.example}
456     ## `lib.strings.makeSearchPath` usage example
458     ```nix
459     makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
460     => "/root/bin:/usr/bin:/usr/local/bin"
461     makeSearchPath "bin" [""]
462     => "/bin"
463     ```
465     :::
466   */
467   makeSearchPath =
468     subDir:
469     paths:
470     concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths));
472   /**
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
477     the default.
480     # Inputs
482     `output`
483     : Package output to use
485     `subDir`
486     : Directory name to append
488     `pkgs`
489     : List of packages
491     # Type
493     ```
494     makeSearchPathOutput :: string -> string -> [package] -> string
495     ```
497     # Examples
498     :::{.example}
499     ## `lib.strings.makeSearchPathOutput` usage example
501     ```nix
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"
504     ```
506     :::
507   */
508   makeSearchPathOutput =
509     output:
510     subDir:
511     pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs);
513   /**
514     Construct a library search path (such as RPATH) containing the
515     libraries for a set of packages
517     # Inputs
519     `packages`
520     : List of packages
522     # Type
524     ```
525     makeLibraryPath :: [package] -> string
526     ```
528     # Examples
529     :::{.example}
530     ## `lib.strings.makeLibraryPath` usage example
532     ```nix
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"
538     ```
540     :::
541   */
542   makeLibraryPath = makeSearchPathOutput "lib" "lib";
544   /**
545     Construct an include search path (such as C_INCLUDE_PATH) containing the
546     header files for a set of packages or paths.
548     # Inputs
550     `packages`
551     : List of packages
553     # Type
555     ```
556     makeIncludePath :: [package] -> string
557     ```
559     # Examples
560     :::{.example}
561     ## `lib.strings.makeIncludePath` usage example
563     ```nix
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"
569     ```
571     :::
572   */
573   makeIncludePath = makeSearchPathOutput "dev" "include";
575   /**
576     Construct a binary search path (such as $PATH) containing the
577     binaries for a set of packages.
579     # Inputs
581     `packages`
582     : List of packages
584     # Type
586     ```
587     makeBinPath :: [package] -> string
588     ```
590     # Examples
591     :::{.example}
592     ## `lib.strings.makeBinPath` usage example
594     ```nix
595     makeBinPath ["/root" "/usr" "/usr/local"]
596     => "/root/bin:/usr/bin:/usr/local/bin"
597     ```
599     :::
600   */
601   makeBinPath = makeSearchPathOutput "bin" "bin";
603   /**
604     Normalize path, removing extraneous /s
607     # Inputs
609     `s`
610     : 1\. Function argument
612     # Type
614     ```
615     normalizePath :: string -> string
616     ```
618     # Examples
619     :::{.example}
620     ## `lib.strings.normalizePath` usage example
622     ```nix
623     normalizePath "/a//b///c/"
624     => "/a/b/c/"
625     ```
627     :::
628   */
629   normalizePath = s:
630     warnIf
631       (isPath s)
632       ''
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.''
637       (
638         builtins.foldl'
639           (x: y: if y == "/" && hasSuffix "/" x then x else x+y)
640           ""
641           (stringToCharacters s)
642       );
644   /**
645     Depending on the boolean `cond', return either the given string
646     or the empty string. Useful to concatenate against a bigger string.
649     # Inputs
651     `cond`
652     : Condition
654     `string`
655     : String to return if condition is true
657     # Type
659     ```
660     optionalString :: bool -> string -> string
661     ```
663     # Examples
664     :::{.example}
665     ## `lib.strings.optionalString` usage example
667     ```nix
668     optionalString true "some-string"
669     => "some-string"
670     optionalString false "some-string"
671     => ""
672     ```
674     :::
675   */
676   optionalString =
677     cond:
678     string: if cond then string else "";
680   /**
681     Determine whether a string has given prefix.
684     # Inputs
686     `pref`
687     : Prefix to check for
689     `str`
690     : Input string
692     # Type
694     ```
695     hasPrefix :: string -> string -> bool
696     ```
698     # Examples
699     :::{.example}
700     ## `lib.strings.hasPrefix` usage example
702     ```nix
703     hasPrefix "foo" "foobar"
704     => true
705     hasPrefix "foo" "barfoo"
706     => false
707     ```
709     :::
710   */
711   hasPrefix =
712     pref:
713     str:
714     # Before 23.05, paths would be copied to the store before converting them
715     # to strings and comparing. This was surprising and confusing.
716     warnIf
717       (isPath pref)
718       ''
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);
726   /**
727     Determine whether a string has given suffix.
730     # Inputs
732     `suffix`
733     : Suffix to check for
735     `content`
736     : Input string
738     # Type
740     ```
741     hasSuffix :: string -> string -> bool
742     ```
744     # Examples
745     :::{.example}
746     ## `lib.strings.hasSuffix` usage example
748     ```nix
749     hasSuffix "foo" "foobar"
750     => false
751     hasSuffix "foo" "barfoo"
752     => true
753     ```
755     :::
756   */
757   hasSuffix =
758     suffix:
759     content:
760     let
761       lenContent = stringLength content;
762       lenSuffix = stringLength suffix;
763     in
764     # Before 23.05, paths would be copied to the store before converting them
765     # to strings and comparing. This was surprising and confusing.
766     warnIf
767       (isPath suffix)
768       ''
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.''
773       (
774         lenContent >= lenSuffix
775         && substring (lenContent - lenSuffix) lenContent content == suffix
776       );
778   /**
779     Determine whether a string contains the given infix
782     # Inputs
784     `infix`
785     : 1\. Function argument
787     `content`
788     : 2\. Function argument
790     # Type
792     ```
793     hasInfix :: string -> string -> bool
794     ```
796     # Examples
797     :::{.example}
798     ## `lib.strings.hasInfix` usage example
800     ```nix
801     hasInfix "bc" "abcd"
802     => true
803     hasInfix "ab" "abcd"
804     => true
805     hasInfix "cd" "abcd"
806     => true
807     hasInfix "foo" "abcd"
808     => false
809     ```
811     :::
812   */
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.
816     warnIf
817       (isPath infix)
818       ''
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);
825   /**
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
832     handle unicode.
835     # Inputs
837     `s`
838     : 1\. Function argument
840     # Type
842     ```
843     stringToCharacters :: string -> [string]
844     ```
846     # Examples
847     :::{.example}
848     ## `lib.strings.stringToCharacters` usage example
850     ```nix
851     stringToCharacters ""
852     => [ ]
853     stringToCharacters "abc"
854     => [ "a" "b" "c" ]
855     stringToCharacters "🦄"
856     => [ "�" "�" "�" "�" ]
857     ```
859     :::
860   */
861   stringToCharacters = s:
862     genList (p: substring p 1 s) (stringLength s);
864   /**
865     Manipulate a string character by character and replace them by
866     strings before concatenating the results.
869     # Inputs
871     `f`
872     : Function to map over each individual character
874     `s`
875     : Input string
877     # Type
879     ```
880     stringAsChars :: (string -> string) -> string -> string
881     ```
883     # Examples
884     :::{.example}
885     ## `lib.strings.stringAsChars` usage example
887     ```nix
888     stringAsChars (x: if x == "a" then "i" else x) "nax"
889     => "nix"
890     ```
892     :::
893   */
894   stringAsChars =
895     # Function to map over each individual character
896     f:
897     # Input string
898     s: concatStrings (
899       map f (stringToCharacters s)
900     );
902   /**
903     Convert char to ascii value, must be in printable range
906     # Inputs
908     `c`
909     : 1\. Function argument
911     # Type
913     ```
914     charToInt :: string -> int
915     ```
917     # Examples
918     :::{.example}
919     ## `lib.strings.charToInt` usage example
921     ```nix
922     charToInt "A"
923     => 65
924     charToInt "("
925     => 40
926     ```
928     :::
929   */
930   charToInt = c: builtins.getAttr c asciiTable;
932   /**
933     Escape occurrence of the elements of `list` in `string` by
934     prefixing it with a backslash.
937     # Inputs
939     `list`
940     : 1\. Function argument
942     `string`
943     : 2\. Function argument
945     # Type
947     ```
948     escape :: [string] -> string -> string
949     ```
951     # Examples
952     :::{.example}
953     ## `lib.strings.escape` usage example
955     ```nix
956     escape ["(" ")"] "(foo)"
957     => "\\(foo\\)"
958     ```
960     :::
961   */
962   escape = list: replaceStrings list (map (c: "\\${c}") list);
964   /**
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.
970     # Inputs
972     `list`
973     : 1\. Function argument
975     `string`
976     : 2\. Function argument
978     # Type
980     ```
981     escapeC = [string] -> string -> string
982     ```
984     # Examples
985     :::{.example}
986     ## `lib.strings.escapeC` usage example
988     ```nix
989     escapeC [" "] "foo bar"
990     => "foo\\x20bar"
991     ```
993     :::
994   */
995   escapeC = list: replaceStrings list (map (c: "\\x${ toLower (lib.toHexString (charToInt c))}") list);
997   /**
998     Escape the `string` so it can be safely placed inside a URL
999     query.
1001     # Inputs
1003     `string`
1004     : 1\. Function argument
1006     # Type
1008     ```
1009     escapeURL :: string -> string
1010     ```
1012     # Examples
1013     :::{.example}
1014     ## `lib.strings.escapeURL` usage example
1016     ```nix
1017     escapeURL "foo/bar baz"
1018     => "foo%2Fbar%20baz"
1019     ```
1021     :::
1022   */
1023   escapeURL = let
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;
1026   in
1027     replaceStrings (builtins.attrNames toEscape) (lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") toEscape);
1029   /**
1030     Quote `string` to be used safely within the Bourne shell if it has any
1031     special characters.
1034     # Inputs
1036     `string`
1037     : 1\. Function argument
1039     # Type
1041     ```
1042     escapeShellArg :: string -> string
1043     ```
1045     # Examples
1046     :::{.example}
1047     ## `lib.strings.escapeShellArg` usage example
1049     ```nix
1050     escapeShellArg "esc'ape\nme"
1051     => "'esc'\\''ape\nme'"
1052     ```
1054     :::
1055   */
1056   escapeShellArg = arg:
1057     let
1058       string = toString arg;
1059     in
1060       if match "[[:alnum:],._+:@%/-]+" string == null
1061       then "'${replaceStrings ["'"] ["'\\''"] string}'"
1062       else string;
1064   /**
1065     Quote all arguments that have special characters to be safely passed to the
1066     Bourne shell.
1068     # Inputs
1070     `args`
1071     : 1\. Function argument
1073     # Type
1075     ```
1076     escapeShellArgs :: [string] -> string
1077     ```
1079     # Examples
1080     :::{.example}
1081     ## `lib.strings.escapeShellArgs` usage example
1083     ```nix
1084     escapeShellArgs ["one" "two three" "four'five"]
1085     => "one 'two three' 'four'\\''five'"
1086     ```
1088     :::
1089   */
1090   escapeShellArgs = concatMapStringsSep " " escapeShellArg;
1092   /**
1093     Test whether the given `name` is a valid POSIX shell variable name.
1096     # Inputs
1098     `name`
1099     : 1\. Function argument
1101     # Type
1103     ```
1104     string -> bool
1105     ```
1107     # Examples
1108     :::{.example}
1109     ## `lib.strings.isValidPosixName` usage example
1111     ```nix
1112     isValidPosixName "foo_bar000"
1113     => true
1114     isValidPosixName "0-bad.jpg"
1115     => false
1116     ```
1118     :::
1119   */
1120   isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
1122   /**
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).
1134     # Inputs
1136     `name`
1137     : 1\. Function argument
1139     `value`
1140     : 2\. Function argument
1142     # Type
1144     ```
1145     string -> ( string | [string] | { ${name} :: string; } ) -> string
1146     ```
1148     # Examples
1149     :::{.example}
1150     ## `lib.strings.toShellVar` usage example
1152     ```nix
1153     ''
1154       ${toShellVar "foo" "some string"}
1155       [[ "$foo" == "some string" ]]
1156     ''
1157     ```
1159     :::
1160   */
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}"
1167         ) value)
1168       })"
1169     else if isList value then
1170       "declare -a ${name}=(${escapeShellArgs value})"
1171     else
1172       "${name}=${escapeShellArg value}"
1173     );
1175   /**
1176     Translate an attribute set `vars` into corresponding shell variable declarations
1177     using `toShellVar`.
1180     # Inputs
1182     `vars`
1183     : 1\. Function argument
1185     # Type
1187     ```
1188     toShellVars :: {
1189       ${name} :: string | [ string ] | { ${key} :: string; };
1190     } -> string
1191     ```
1193     # Examples
1194     :::{.example}
1195     ## `lib.strings.toShellVars` usage example
1197     ```nix
1198     let
1199       foo = "value";
1200       bar = foo;
1201     in ''
1202       ${toShellVars { inherit foo bar; }}
1203       [[ "$foo" == "$bar" ]]
1204     ''
1205     ```
1207     :::
1208   */
1209   toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
1211   /**
1212     Turn a string `s` into a Nix expression representing that string
1214     # Inputs
1216     `s`
1217     : 1\. Function argument
1219     # Type
1221     ```
1222     escapeNixString :: string -> string
1223     ```
1225     # Examples
1226     :::{.example}
1227     ## `lib.strings.escapeNixString` usage example
1229     ```nix
1230     escapeNixString "hello\${}\n"
1231     => "\"hello\\\${}\\n\""
1232     ```
1234     :::
1235   */
1236   escapeNixString = s: escape ["$"] (toJSON s);
1238   /**
1239     Turn a string `s` into an exact regular expression
1241     # Inputs
1243     `s`
1244     : 1\. Function argument
1246     # Type
1248     ```
1249     escapeRegex :: string -> string
1250     ```
1252     # Examples
1253     :::{.example}
1254     ## `lib.strings.escapeRegex` usage example
1256     ```nix
1257     escapeRegex "[^a-z]*"
1258     => "\\[\\^a-z]\\*"
1259     ```
1261     :::
1262   */
1263   escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
1265   /**
1266     Quotes a string `s` if it can't be used as an identifier directly.
1269     # Inputs
1271     `s`
1272     : 1\. Function argument
1274     # Type
1276     ```
1277     escapeNixIdentifier :: string -> string
1278     ```
1280     # Examples
1281     :::{.example}
1282     ## `lib.strings.escapeNixIdentifier` usage example
1284     ```nix
1285     escapeNixIdentifier "hello"
1286     => "hello"
1287     escapeNixIdentifier "0abc"
1288     => "\"0abc\""
1289     ```
1291     :::
1292   */
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;
1298   /**
1299     Escapes a string `s` such that it is safe to include verbatim in an XML
1300     document.
1302     # Inputs
1304     `s`
1305     : 1\. Function argument
1307     # Type
1309     ```
1310     escapeXML :: string -> string
1311     ```
1313     # Examples
1314     :::{.example}
1315     ## `lib.strings.escapeXML` usage example
1317     ```nix
1318     escapeXML ''"test" 'test' < & >''
1319     => "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;"
1320     ```
1322     :::
1323   */
1324   escapeXML = builtins.replaceStrings
1325     ["\"" "'" "<" ">" "&"]
1326     ["&quot;" "&apos;" "&lt;" "&gt;" "&amp;"];
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";
1335   /**
1336     Converts an ASCII string `s` to lower-case.
1338     # Inputs
1340     `s`
1341     : The string to convert to lower-case.
1343     # Type
1345     ```
1346     toLower :: string -> string
1347     ```
1349     # Examples
1350     :::{.example}
1351     ## `lib.strings.toLower` usage example
1353     ```nix
1354     toLower "HOME"
1355     => "home"
1356     ```
1358     :::
1359   */
1360   toLower = replaceStrings upperChars lowerChars;
1362   /**
1363     Converts an ASCII string `s` to upper-case.
1365     # Inputs
1367     `s`
1368     : The string to convert to upper-case.
1371     # Type
1373     ```
1374     toUpper :: string -> string
1375     ```
1377     # Examples
1378     :::{.example}
1379     ## `lib.strings.toUpper` usage example
1381     ```nix
1382     toUpper "home"
1383     => "HOME"
1384     ```
1386     :::
1387   */
1388   toUpper = replaceStrings lowerChars upperChars;
1390   /**
1391     Appends string context from string like object `src` to `target`.
1393     :::{.warning}
1394     This is an implementation
1395     detail of Nix and should be used carefully.
1396     :::
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
1401     inputSrcs.
1404     # Inputs
1406     `src`
1407     : The string to take the context from. If the argument is not a string,
1408       it will be implicitly converted to a string.
1410     `target`
1411     : The string to append the context to. If the argument is not a string,
1412       it will be implicitly converted to a string.
1414     # Type
1416     ```
1417     addContextFrom :: string -> string -> string
1418     ```
1420     # Examples
1421     :::{.example}
1422     ## `lib.strings.addContextFrom` usage example
1424     ```nix
1425     pkgs = import <nixpkgs> { };
1426     addContextFrom pkgs.coreutils "bar"
1427     => "bar"
1428     ```
1430     The context can be displayed using the `toString` function:
1432     ```nix
1433     nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
1434     {
1435       "/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
1436     }
1437     ```
1439     :::
1440   */
1441   addContextFrom = src: target: substring 0 0 src + target;
1443   /**
1444     Cut a string with a separator and produces a list of strings which
1445     were separated by this separator.
1447     # Inputs
1449     `sep`
1450     : 1\. Function argument
1452     `s`
1453     : 2\. Function argument
1455     # Type
1457     ```
1458     splitString :: string -> string -> [string]
1459     ```
1461     # Examples
1462     :::{.example}
1463     ## `lib.strings.splitString` usage example
1465     ```nix
1466     splitString "." "foo.bar.baz"
1467     => [ "foo" "bar" "baz" ]
1468     splitString "/" "/usr/local/bin"
1469     => [ "" "usr" "local" "bin" ]
1470     ```
1472     :::
1473   */
1474   splitString = sep: s:
1475     let
1476       splits = builtins.filter builtins.isString (builtins.split (escapeRegex (toString sep)) (toString s));
1477     in
1478       map (addContextFrom s) splits;
1481   /**
1482     Return a string without the specified prefix, if the prefix matches.
1484     # Inputs
1486     `prefix`
1487     : Prefix to remove if it matches
1489     `str`
1490     : Input string
1492     # Type
1494     ```
1495     removePrefix :: string -> string -> string
1496     ```
1498     # Examples
1499     :::{.example}
1500     ## `lib.strings.removePrefix` usage example
1502     ```nix
1503     removePrefix "foo." "foo.bar.baz"
1504     => "bar.baz"
1505     removePrefix "xxx" "foo.bar.baz"
1506     => "foo.bar.baz"
1507     ```
1509     :::
1510   */
1511   removePrefix =
1512     prefix:
1513     str:
1514     # Before 23.05, paths would be copied to the store before converting them
1515     # to strings and comparing. This was surprising and confusing.
1516     warnIf
1517       (isPath prefix)
1518       ''
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.''
1523     (let
1524       preLen = stringLength prefix;
1525     in
1526       if substring 0 preLen str == prefix then
1527         # -1 will take the string until the end
1528         substring preLen (-1) str
1529       else
1530         str);
1532   /**
1533     Return a string without the specified suffix, if the suffix matches.
1536     # Inputs
1538     `suffix`
1539     : Suffix to remove if it matches
1541     `str`
1542     : Input string
1544     # Type
1546     ```
1547     removeSuffix :: string -> string -> string
1548     ```
1550     # Examples
1551     :::{.example}
1552     ## `lib.strings.removeSuffix` usage example
1554     ```nix
1555     removeSuffix "front" "homefront"
1556     => "home"
1557     removeSuffix "xxx" "homefront"
1558     => "homefront"
1559     ```
1561     :::
1562   */
1563   removeSuffix =
1564     suffix:
1565     str:
1566     # Before 23.05, paths would be copied to the store before converting them
1567     # to strings and comparing. This was surprising and confusing.
1568     warnIf
1569       (isPath suffix)
1570       ''
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.''
1575     (let
1576       sufLen = stringLength suffix;
1577       sLen = stringLength str;
1578     in
1579       if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then
1580         substring 0 (sLen - sufLen) str
1581       else
1582         str);
1584   /**
1585     Return true if string `v1` denotes a version older than `v2`.
1588     # Inputs
1590     `v1`
1591     : 1\. Function argument
1593     `v2`
1594     : 2\. Function argument
1596     # Type
1598     ```
1599     versionOlder :: String -> String -> Bool
1600     ```
1602     # Examples
1603     :::{.example}
1604     ## `lib.strings.versionOlder` usage example
1606     ```nix
1607     versionOlder "1.1" "1.2"
1608     => true
1609     versionOlder "1.1" "1.1"
1610     => false
1611     ```
1613     :::
1614   */
1615   versionOlder = v1: v2: compareVersions v2 v1 == 1;
1617   /**
1618     Return true if string v1 denotes a version equal to or newer than v2.
1621     # Inputs
1623     `v1`
1624     : 1\. Function argument
1626     `v2`
1627     : 2\. Function argument
1629     # Type
1631     ```
1632     versionAtLeast :: String -> String -> Bool
1633     ```
1635     # Examples
1636     :::{.example}
1637     ## `lib.strings.versionAtLeast` usage example
1639     ```nix
1640     versionAtLeast "1.1" "1.0"
1641     => true
1642     versionAtLeast "1.1" "1.1"
1643     => true
1644     versionAtLeast "1.1" "1.2"
1645     => false
1646     ```
1648     :::
1649   */
1650   versionAtLeast = v1: v2: !versionOlder v1 v2;
1652   /**
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
1655     argument.
1657     # Inputs
1659     `x`
1660     : 1\. Function argument
1662     # Type
1664     ```
1665     getName :: String | Derivation -> String
1666     ```
1669     # Examples
1670     :::{.example}
1671     ## `lib.strings.getName` usage example
1673     ```nix
1674     getName "youtube-dl-2016.01.01"
1675     => "youtube-dl"
1676     getName pkgs.youtube-dl
1677     => "youtube-dl"
1678     ```
1680     :::
1681   */
1682   getName = let
1683     parse = drv: (parseDrvName drv).name;
1684   in x:
1685     if isString x
1686     then parse x
1687     else x.pname or (parse x.name);
1689   /**
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
1692     argument.
1695     # Inputs
1697     `x`
1698     : 1\. Function argument
1700     # Type
1702     ```
1703     getVersion :: String | Derivation -> String
1704     ```
1706     # Examples
1707     :::{.example}
1708     ## `lib.strings.getVersion` usage example
1710     ```nix
1711     getVersion "youtube-dl-2016.01.01"
1712     => "2016.01.01"
1713     getVersion pkgs.youtube-dl
1714     => "2016.01.01"
1715     ```
1717     :::
1718   */
1719   getVersion = let
1720     parse = drv: (parseDrvName drv).version;
1721   in x:
1722     if isString x
1723     then parse x
1724     else x.version or (parse x.name);
1726   /**
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.
1732     # Inputs
1734     `url`
1735     : 1\. Function argument
1737     `sep`
1738     : 2\. Function argument
1740     # Type
1742     ```
1743     nameFromURL :: String -> String
1744     ```
1746     # Examples
1747     :::{.example}
1748     ## `lib.strings.nameFromURL` usage example
1750     ```nix
1751     nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
1752     => "nix"
1753     nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
1754     => "nix-1.7-x86"
1755     ```
1757     :::
1758   */
1759   nameFromURL = url: sep:
1760     let
1761       components = splitString "/" url;
1762       filename = lib.last components;
1763       name = head (splitString sep filename);
1764     in assert name != filename; name;
1766   /**
1767     Create a `"-D<feature>:<type>=<value>"` string that can be passed to typical
1768     CMake invocations.
1770     # Inputs
1772     `feature`
1773     : The feature to be set
1775     `type`
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
1781     `value`
1782     : The desired value
1784     # Type
1786     ```
1787     cmakeOptionType :: string -> string -> string -> string
1788     ```
1790     # Examples
1791     :::{.example}
1792     ## `lib.strings.cmakeOptionType` usage example
1794     ```nix
1795     cmakeOptionType "string" "ENGINE" "sdl2"
1796     => "-DENGINE:STRING=sdl2"
1797     ```
1799     :::
1800   */
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}";
1809   /**
1810     Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
1811     CMake invocations.
1814     # Inputs
1816     `condition`
1817     : The condition to be made true or false
1819     `flag`
1820     : The controlling flag of the condition
1822     # Type
1824     ```
1825     cmakeBool :: string -> bool -> string
1826     ```
1828     # Examples
1829     :::{.example}
1830     ## `lib.strings.cmakeBool` usage example
1832     ```nix
1833     cmakeBool "ENABLE_STATIC_LIBS" false
1834     => "-DENABLESTATIC_LIBS:BOOL=FALSE"
1835     ```
1837     :::
1838   */
1839   cmakeBool = condition: flag:
1840     assert (lib.isString condition);
1841     assert (lib.isBool flag);
1842     cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag));
1844   /**
1845     Create a -D<feature>:STRING=<value> string that can be passed to typical
1846     CMake invocations.
1847     This is the most typical usage, so it deserves a special case.
1850     # Inputs
1852     `feature`
1853     : The feature to be set
1855     `value`
1856     : The desired value
1859     # Type
1861     ```
1862     cmakeFeature :: string -> string -> string
1863     ```
1865     # Examples
1866     :::{.example}
1867     ## `lib.strings.cmakeFeature` usage example
1869     ```nix
1870     cmakeFeature "MODULES" "badblock"
1871     => "-DMODULES:STRING=badblock"
1872     ```
1874     :::
1875   */
1876   cmakeFeature = feature: value:
1877     assert (lib.isString feature);
1878     assert (lib.isString value);
1879     cmakeOptionType "string" feature value;
1881   /**
1882     Create a -D<feature>=<value> string that can be passed to typical Meson
1883     invocations.
1886     # Inputs
1888     `feature`
1889     : The feature to be set
1891     `value`
1892     : The desired value
1894     # Type
1896     ```
1897     mesonOption :: string -> string -> string
1898     ```
1900     # Examples
1901     :::{.example}
1902     ## `lib.strings.mesonOption` usage example
1904     ```nix
1905     mesonOption "engine" "opengl"
1906     => "-Dengine=opengl"
1907     ```
1909     :::
1910   */
1911   mesonOption = feature: value:
1912     assert (lib.isString feature);
1913     assert (lib.isString value);
1914     "-D${feature}=${value}";
1916   /**
1917     Create a -D<condition>={true,false} string that can be passed to typical
1918     Meson invocations.
1921     # Inputs
1923     `condition`
1924     : The condition to be made true or false
1926     `flag`
1927     : The controlling flag of the condition
1929     # Type
1931     ```
1932     mesonBool :: string -> bool -> string
1933     ```
1935     # Examples
1936     :::{.example}
1937     ## `lib.strings.mesonBool` usage example
1939     ```nix
1940     mesonBool "hardened" true
1941     => "-Dhardened=true"
1942     mesonBool "static" false
1943     => "-Dstatic=false"
1944     ```
1946     :::
1947   */
1948   mesonBool = condition: flag:
1949     assert (lib.isString condition);
1950     assert (lib.isBool flag);
1951     mesonOption condition (lib.boolToString flag);
1953   /**
1954     Create a -D<feature>={enabled,disabled} string that can be passed to
1955     typical Meson invocations.
1958     # Inputs
1960     `feature`
1961     : The feature to be enabled or disabled
1963     `flag`
1964     : The controlling flag
1966     # Type
1968     ```
1969     mesonEnable :: string -> bool -> string
1970     ```
1972     # Examples
1973     :::{.example}
1974     ## `lib.strings.mesonEnable` usage example
1976     ```nix
1977     mesonEnable "docs" true
1978     => "-Ddocs=enabled"
1979     mesonEnable "savage" false
1980     => "-Dsavage=disabled"
1981     ```
1983     :::
1984   */
1985   mesonEnable = feature: flag:
1986     assert (lib.isString feature);
1987     assert (lib.isBool flag);
1988     mesonOption feature (if flag then "enabled" else "disabled");
1990   /**
1991     Create an --{enable,disable}-<feature> string that can be passed to
1992     standard GNU Autoconf scripts.
1995     # Inputs
1997     `flag`
1998     : 1\. Function argument
2000     `feature`
2001     : 2\. Function argument
2003     # Type
2005     ```
2006     enableFeature :: bool -> string -> string
2007     ```
2009     # Examples
2010     :::{.example}
2011     ## `lib.strings.enableFeature` usage example
2013     ```nix
2014     enableFeature true "shared"
2015     => "--enable-shared"
2016     enableFeature false "shared"
2017     => "--disable-shared"
2018     ```
2020     :::
2021   */
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}";
2027   /**
2028     Create an --{enable-<feature>=<value>,disable-<feature>} string that can be passed to
2029     standard GNU Autoconf scripts.
2032     # Inputs
2034     `flag`
2035     : 1\. Function argument
2037     `feature`
2038     : 2\. Function argument
2040     `value`
2041     : 3\. Function argument
2043     # Type
2045     ```
2046     enableFeatureAs :: bool -> string -> string -> string
2047     ```
2049     # Examples
2050     :::{.example}
2051     ## `lib.strings.enableFeatureAs` usage example
2053     ```nix
2054     enableFeatureAs true "shared" "foo"
2055     => "--enable-shared=foo"
2056     enableFeatureAs false "shared" (throw "ignored")
2057     => "--disable-shared"
2058     ```
2060     :::
2061   */
2062   enableFeatureAs = flag: feature: value:
2063     enableFeature flag feature + optionalString flag "=${value}";
2065   /**
2066     Create an --{with,without}-<feature> string that can be passed to
2067     standard GNU Autoconf scripts.
2070     # Inputs
2072     `flag`
2073     : 1\. Function argument
2075     `feature`
2076     : 2\. Function argument
2079     # Type
2081     ```
2082     withFeature :: bool -> string -> string
2083     ```
2085     # Examples
2086     :::{.example}
2087     ## `lib.strings.withFeature` usage example
2089     ```nix
2090     withFeature true "shared"
2091     => "--with-shared"
2092     withFeature false "shared"
2093     => "--without-shared"
2094     ```
2096     :::
2097   */
2098   withFeature = flag: feature:
2099     assert isString feature; # e.g. passing openssl instead of "openssl"
2100     "--${if flag then "with" else "without"}-${feature}";
2102   /**
2103     Create an --{with-<feature>=<value>,without-<feature>} string that can be passed to
2104     standard GNU Autoconf scripts.
2107     # Inputs
2109     `flag`
2110     : 1\. Function argument
2112     `feature`
2113     : 2\. Function argument
2115     `value`
2116     : 3\. Function argument
2118     # Type
2120     ```
2121     withFeatureAs :: bool -> string -> string -> string
2122     ```
2125     # Examples
2126     :::{.example}
2127     ## `lib.strings.withFeatureAs` usage example
2129     ```nix
2130     withFeatureAs true "shared" "foo"
2131     => "--with-shared=foo"
2132     withFeatureAs false "shared" (throw "ignored")
2133     => "--without-shared"
2134     ```
2136     :::
2137   */
2138   withFeatureAs = flag: feature: value:
2139     withFeature flag feature + optionalString flag "=${value}";
2141   /**
2142     Create a fixed width string with additional prefix to match
2143     required width.
2145     This function will fail if the input string is longer than the
2146     requested length.
2149     # Inputs
2151     `width`
2152     : 1\. Function argument
2154     `filler`
2155     : 2\. Function argument
2157     `str`
2158     : 3\. Function argument
2160     # Type
2162     ```
2163     fixedWidthString :: int -> string -> string -> string
2164     ```
2166     # Examples
2167     :::{.example}
2168     ## `lib.strings.fixedWidthString` usage example
2170     ```nix
2171     fixedWidthString 5 "0" (toString 15)
2172     => "00015"
2173     ```
2175     :::
2176   */
2177   fixedWidthString = width: filler: str:
2178     let
2179       strw = lib.stringLength str;
2180       reqWidth = width - (lib.stringLength filler);
2181     in
2182       assert lib.assertMsg (strw <= width)
2183         "fixedWidthString: requested string length (${
2184           toString width}) must not be shorter than actual length (${
2185             toString strw})";
2186       if strw == width then str else filler + fixedWidthString reqWidth filler str;
2188   /**
2189     Format a number adding leading zeroes up to fixed width.
2192     # Inputs
2194     `width`
2195     : 1\. Function argument
2197     `n`
2198     : 2\. Function argument
2200     # Type
2202     ```
2203     fixedWidthNumber :: int -> int -> string
2204     ```
2206     # Examples
2207     :::{.example}
2208     ## `lib.strings.fixedWidthNumber` usage example
2210     ```nix
2211     fixedWidthNumber 5 15
2212     => "00015"
2213     ```
2215     :::
2216   */
2217   fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
2219   /**
2220     Convert a float to a string, but emit a warning when precision is lost
2221     during the conversion
2224     # Inputs
2226     `float`
2227     : 1\. Function argument
2230     # Type
2232     ```
2233     floatToString :: float -> string
2234     ```
2236     # Examples
2237     :::{.example}
2238     ## `lib.strings.floatToString` usage example
2240     ```nix
2241     floatToString 0.000001
2242     => "0.000001"
2243     floatToString 0.0000001
2244     => trace: warning: Imprecise conversion from float to string 0.000000
2245        "0.000000"
2246     ```
2248     :::
2249   */
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}"
2254     result;
2256   /**
2257     Check whether a value `val` can be coerced to a string.
2259     :::{.warning}
2260     Soft-deprecated function. While the original implementation is available as
2261     `isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
2262     :::
2264     # Inputs
2266     `val`
2267     : 1\. Function argument
2269     # Type
2271     ```
2272     isCoercibleToString :: a -> bool
2273     ```
2274   */
2275   isCoercibleToString = lib.warnIf (lib.isInOldestRelease 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;
2279   /**
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.
2285     # Inputs
2287     `val`
2288     : 1\. Function argument
2290     # Type
2292     ```
2293     isConvertibleWithToString :: a -> bool
2294     ```
2295   */
2296   isConvertibleWithToString = let
2297     types = [ "null" "int" "float" "bool" ];
2298   in x:
2299     isStringLike x ||
2300     elem (typeOf x) types ||
2301     (isList x && lib.all isConvertibleWithToString x);
2303   /**
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.
2311     # Inputs
2313     `x`
2314     : 1\. Function argument
2316     # Type
2318     ```
2319     isStringLike :: a -> bool
2320     ```
2321   */
2322   isStringLike = x:
2323     isString x ||
2324     isPath x ||
2325     x ? outPath ||
2326     x ? __toString;
2328   /**
2329     Check whether a value `x` is a store path.
2332     # Inputs
2334     `x`
2335     : 1\. Function argument
2337     # Type
2339     ```
2340     isStorePath :: a -> bool
2341     ```
2343     # Examples
2344     :::{.example}
2345     ## `lib.strings.isStorePath` usage example
2347     ```nix
2348     isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
2349     => false
2350     isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"
2351     => true
2352     isStorePath pkgs.python
2353     => true
2354     isStorePath [] || isStorePath 42 || isStorePath {} || â€¦
2355     => false
2356     ```
2358     :::
2359   */
2360   isStorePath = x:
2361     if isStringLike x then
2362       let str = toString x; in
2363       substring 0 1 str == "/"
2364       && dirOf str == storeDir
2365     else
2366       false;
2368   /**
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.
2372     # Inputs
2374     `str`
2375     : A string to be interpreted as an int.
2377     # Type
2379     ```
2380     toInt :: string -> int
2381     ```
2383     # Examples
2384     :::{.example}
2385     ## `lib.strings.toInt` usage example
2387     ```nix
2388     toInt "1337"
2389     => 1337
2391     toInt "-4"
2392     => -4
2394     toInt " 123 "
2395     => 123
2397     toInt "00024"
2398     => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
2400     toInt "3.14"
2401     => error: floating point JSON numbers are not supported
2402     ```
2404     :::
2405   */
2406   toInt =
2407     let
2408       matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*";
2409       matchLeadingZero = match "0[[:digit:]]+";
2410     in
2411     str:
2412     let
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.";
2425     in
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
2435       # Return result.
2436       else parsedInput;
2439   /**
2440     Parse a string as a base 10 int. This supports parsing of zero-padded integers.
2442     # Inputs
2444     `str`
2445     : A string to be interpreted as an int.
2447     # Type
2449     ```
2450     toIntBase10 :: string -> int
2451     ```
2453     # Examples
2454     :::{.example}
2455     ## `lib.strings.toIntBase10` usage example
2457     ```nix
2458     toIntBase10 "1337"
2459     => 1337
2461     toIntBase10 "-4"
2462     => -4
2464     toIntBase10 " 123 "
2465     => 123
2467     toIntBase10 "00024"
2468     => 24
2470     toIntBase10 "3.14"
2471     => error: floating point JSON numbers are not supported
2472     ```
2474     :::
2475   */
2476   toIntBase10 =
2477     let
2478       matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*";
2479       matchZero = match "0+";
2480     in
2481     str:
2482     let
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.";
2496     in
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.
2501       else if isZero
2502       then 0
2503       # Error if parse function fails.
2504       else if !isInt parsedInput
2505       then throw generalError
2506       # Return result.
2507       else parsedInput;
2509   /**
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.
2514     :::{.warning}
2515     This function is deprecated and should be avoided.
2516     :::
2518     :::{.note}
2519     This function is not performant and should be avoided.
2520     :::
2522     # Inputs
2524     `rootPath`
2525     : 1\. Function argument
2527     `file`
2528     : 2\. Function argument
2530     # Type
2532     ```
2533     readPathsFromFile :: string -> string -> [string]
2534     ```
2536     # Examples
2537     :::{.example}
2538     ## `lib.strings.readPathsFromFile` usage example
2540     ```nix
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" ]
2548     ```
2550     :::
2551   */
2552   readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead."
2553     (rootPath: file:
2554       let
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;
2559       in
2560         absolutePaths);
2562   /**
2563     Read the contents of a file removing the trailing \n
2566     # Inputs
2568     `file`
2569     : 1\. Function argument
2571     # Type
2573     ```
2574     fileContents :: path -> string
2575     ```
2577     # Examples
2578     :::{.example}
2579     ## `lib.strings.fileContents` usage example
2581     ```nix
2582     $ echo "1.0" > ./version
2584     fileContents ./version
2585     => "1.0"
2586     ```
2588     :::
2589   */
2590   fileContents = file: removeSuffix "\n" (readFile file);
2593   /**
2594     Creates a valid derivation name from a potentially invalid one.
2596     # Inputs
2598     `string`
2599     : 1\. Function argument
2601     # Type
2603     ```
2604     sanitizeDerivationName :: String -> String
2605     ```
2607     # Examples
2608     :::{.example}
2609     ## `lib.strings.sanitizeDerivationName` usage example
2611     ```nix
2612     sanitizeDerivationName "../hello.bar # foo"
2613     => "-hello.bar-foo"
2614     sanitizeDerivationName ""
2615     => "unknown"
2616     sanitizeDerivationName pkgs.hello
2617     => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10"
2618     ```
2620     :::
2621   */
2622   sanitizeDerivationName =
2623   let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*";
2624   in
2625   string:
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)
2645   ];
2647   /**
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
2654     # Inputs
2656     `a`
2657     : 1\. Function argument
2659     `b`
2660     : 2\. Function argument
2662     # Type
2664     ```
2665     levenshtein :: string -> string -> int
2666     ```
2668     # Examples
2669     :::{.example}
2670     ## `lib.strings.levenshtein` usage example
2672     ```nix
2673     levenshtein "foo" "foo"
2674     => 0
2675     levenshtein "book" "hook"
2676     => 1
2677     levenshtein "hello" "Heyo"
2678     => 3
2679     ```
2681     :::
2682   */
2683   levenshtein = a: b: let
2684     # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1)
2685     arr = lib.genList (i:
2686       lib.genList (j:
2687         dist i j
2688       ) (stringLength b + 1)
2689     ) (stringLength a + 1);
2690     d = x: y: lib.elemAt (lib.elemAt arr x) y;
2691     dist = i: j:
2692       let c = if substring (i - 1) 1 a == substring (j - 1) 1 b
2693         then 0 else 1;
2694       in
2695       if j == 0 then i
2696       else if i == 0 then j
2697       else lib.min
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);
2702   /**
2703     Returns the length of the prefix that appears in both strings `a` and `b`.
2706     # Inputs
2708     `a`
2709     : 1\. Function argument
2711     `b`
2712     : 2\. Function argument
2714     # Type
2716     ```
2717     commonPrefixLength :: string -> string -> int
2718     ```
2719   */
2720   commonPrefixLength = a: b:
2721     let
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;
2724     in go 0;
2726   /**
2727     Returns the length of the suffix common to both strings `a` and `b`.
2730     # Inputs
2732     `a`
2733     : 1\. Function argument
2735     `b`
2736     : 2\. Function argument
2738     # Type
2740     ```
2741     commonSuffixLength :: string -> string -> int
2742     ```
2743   */
2744   commonSuffixLength = a: b:
2745     let
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;
2748     in go 0;
2750   /**
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
2755     # Inputs
2757     `k`
2758     : Distance threshold
2760     `a`
2761     : String `a`
2763     `b`
2764     : String `b`
2766     # Type
2768     ```
2769     levenshteinAtMost :: int -> string -> string -> bool
2770     ```
2772     # Examples
2773     :::{.example}
2774     ## `lib.strings.levenshteinAtMost` usage example
2776     ```nix
2777     levenshteinAtMost 0 "foo" "foo"
2778     => true
2779     levenshteinAtMost 1 "foo" "boa"
2780     => false
2781     levenshteinAtMost 2 "foo" "boa"
2782     => true
2783     levenshteinAtMost 2 "This is a sentence" "this is a sentense."
2784     => false
2785     levenshteinAtMost 3 "This is a sentence" "this is a sentense."
2786     => true
2787     ```
2789     :::
2790   */
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
2799     # implementation.
2800     infixDifferAtMost2 = x: y:
2801       let
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
2806         diff = xlen - ylen;
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;
2817       in
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
2828         # vice versa
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
2833       let f = a: b:
2834         let
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;
2842         in
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;
2850       in f;