electron-source.electron_29: remove as it's EOL
[NixPkgs.git] / lib / strings.nix
blobaafbdffaa7bc05b7883cedefbaf3f88613bdfba0
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     s:
412     let
413       # Define our own whitespace character class instead of using
414       # `[:space:]`, which is not well-defined.
415       chars = " \t\r\n";
417       # To match up until trailing whitespace, we need to capture a
418       # group that ends with a non-whitespace character.
419       regex =
420         if start && end then
421           "[${chars}]*(.*[^${chars}])[${chars}]*"
422         else if start then
423           "[${chars}]*(.*)"
424         else if end then
425           "(.*[^${chars}])[${chars}]*"
426         else
427           "(.*)";
429       # If the string was empty or entirely whitespace,
430       # then the regex may not match and `res` will be `null`.
431       res = match regex s;
432     in
433       optionalString (res != null) (head res);
435   /**
436     Construct a Unix-style, colon-separated search path consisting of
437     the given `subDir` appended to each of the given paths.
439     # Inputs
441     `subDir`
442     : Directory name to append
444     `paths`
445     : List of base paths
447     # Type
449     ```
450     makeSearchPath :: string -> [string] -> string
451     ```
453     # Examples
454     :::{.example}
455     ## `lib.strings.makeSearchPath` usage example
457     ```nix
458     makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
459     => "/root/bin:/usr/bin:/usr/local/bin"
460     makeSearchPath "bin" [""]
461     => "/bin"
462     ```
464     :::
465   */
466   makeSearchPath =
467     subDir:
468     paths:
469     concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths));
471   /**
472     Construct a Unix-style search path by appending the given
473     `subDir` to the specified `output` of each of the packages.
475     If no output by the given name is found, fallback to `.out` and then to
476     the default.
479     # Inputs
481     `output`
482     : Package output to use
484     `subDir`
485     : Directory name to append
487     `pkgs`
488     : List of packages
490     # Type
492     ```
493     makeSearchPathOutput :: string -> string -> [package] -> string
494     ```
496     # Examples
497     :::{.example}
498     ## `lib.strings.makeSearchPathOutput` usage example
500     ```nix
501     makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ]
502     => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin"
503     ```
505     :::
506   */
507   makeSearchPathOutput =
508     output:
509     subDir:
510     pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs);
512   /**
513     Construct a library search path (such as RPATH) containing the
514     libraries for a set of packages
516     # Inputs
518     `packages`
519     : List of packages
521     # Type
523     ```
524     makeLibraryPath :: [package] -> string
525     ```
527     # Examples
528     :::{.example}
529     ## `lib.strings.makeLibraryPath` usage example
531     ```nix
532     makeLibraryPath [ "/usr" "/usr/local" ]
533     => "/usr/lib:/usr/local/lib"
534     pkgs = import <nixpkgs> { }
535     makeLibraryPath [ pkgs.openssl pkgs.zlib ]
536     => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib"
537     ```
539     :::
540   */
541   makeLibraryPath = makeSearchPathOutput "lib" "lib";
543   /**
544     Construct an include search path (such as C_INCLUDE_PATH) containing the
545     header files for a set of packages or paths.
547     # Inputs
549     `packages`
550     : List of packages
552     # Type
554     ```
555     makeIncludePath :: [package] -> string
556     ```
558     # Examples
559     :::{.example}
560     ## `lib.strings.makeIncludePath` usage example
562     ```nix
563     makeIncludePath [ "/usr" "/usr/local" ]
564     => "/usr/include:/usr/local/include"
565     pkgs = import <nixpkgs> { }
566     makeIncludePath [ pkgs.openssl pkgs.zlib ]
567     => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/include:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8-dev/include"
568     ```
570     :::
571   */
572   makeIncludePath = makeSearchPathOutput "dev" "include";
574   /**
575     Construct a binary search path (such as $PATH) containing the
576     binaries for a set of packages.
578     # Inputs
580     `packages`
581     : List of packages
583     # Type
585     ```
586     makeBinPath :: [package] -> string
587     ```
589     # Examples
590     :::{.example}
591     ## `lib.strings.makeBinPath` usage example
593     ```nix
594     makeBinPath ["/root" "/usr" "/usr/local"]
595     => "/root/bin:/usr/bin:/usr/local/bin"
596     ```
598     :::
599   */
600   makeBinPath = makeSearchPathOutput "bin" "bin";
602   /**
603     Normalize path, removing extraneous /s
606     # Inputs
608     `s`
609     : 1\. Function argument
611     # Type
613     ```
614     normalizePath :: string -> string
615     ```
617     # Examples
618     :::{.example}
619     ## `lib.strings.normalizePath` usage example
621     ```nix
622     normalizePath "/a//b///c/"
623     => "/a/b/c/"
624     ```
626     :::
627   */
628   normalizePath = s:
629     warnIf
630       (isPath s)
631       ''
632         lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported.
633             Path values are always normalised in Nix, so there's no need to call this function on them.
634             This function also copies the path to the Nix store and returns the store path, the same as "''${path}" will, which may not be what you want.
635             This behavior is deprecated and will throw an error in the future.''
636       (
637         builtins.foldl'
638           (x: y: if y == "/" && hasSuffix "/" x then x else x+y)
639           ""
640           (stringToCharacters s)
641       );
643   /**
644     Depending on the boolean `cond', return either the given string
645     or the empty string. Useful to concatenate against a bigger string.
648     # Inputs
650     `cond`
651     : Condition
653     `string`
654     : String to return if condition is true
656     # Type
658     ```
659     optionalString :: bool -> string -> string
660     ```
662     # Examples
663     :::{.example}
664     ## `lib.strings.optionalString` usage example
666     ```nix
667     optionalString true "some-string"
668     => "some-string"
669     optionalString false "some-string"
670     => ""
671     ```
673     :::
674   */
675   optionalString =
676     cond:
677     string: if cond then string else "";
679   /**
680     Determine whether a string has given prefix.
683     # Inputs
685     `pref`
686     : Prefix to check for
688     `str`
689     : Input string
691     # Type
693     ```
694     hasPrefix :: string -> string -> bool
695     ```
697     # Examples
698     :::{.example}
699     ## `lib.strings.hasPrefix` usage example
701     ```nix
702     hasPrefix "foo" "foobar"
703     => true
704     hasPrefix "foo" "barfoo"
705     => false
706     ```
708     :::
709   */
710   hasPrefix =
711     pref:
712     str:
713     # Before 23.05, paths would be copied to the store before converting them
714     # to strings and comparing. This was surprising and confusing.
715     warnIf
716       (isPath pref)
717       ''
718         lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported.
719             There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
720             This function also copies the path to the Nix store, which may not be what you want.
721             This behavior is deprecated and will throw an error in the future.
722             You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.''
723       (substring 0 (stringLength pref) str == pref);
725   /**
726     Determine whether a string has given suffix.
729     # Inputs
731     `suffix`
732     : Suffix to check for
734     `content`
735     : Input string
737     # Type
739     ```
740     hasSuffix :: string -> string -> bool
741     ```
743     # Examples
744     :::{.example}
745     ## `lib.strings.hasSuffix` usage example
747     ```nix
748     hasSuffix "foo" "foobar"
749     => false
750     hasSuffix "foo" "barfoo"
751     => true
752     ```
754     :::
755   */
756   hasSuffix =
757     suffix:
758     content:
759     let
760       lenContent = stringLength content;
761       lenSuffix = stringLength suffix;
762     in
763     # Before 23.05, paths would be copied to the store before converting them
764     # to strings and comparing. This was surprising and confusing.
765     warnIf
766       (isPath suffix)
767       ''
768         lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
769             There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
770             This function also copies the path to the Nix store, which may not be what you want.
771             This behavior is deprecated and will throw an error in the future.''
772       (
773         lenContent >= lenSuffix
774         && substring (lenContent - lenSuffix) lenContent content == suffix
775       );
777   /**
778     Determine whether a string contains the given infix
781     # Inputs
783     `infix`
784     : 1\. Function argument
786     `content`
787     : 2\. Function argument
789     # Type
791     ```
792     hasInfix :: string -> string -> bool
793     ```
795     # Examples
796     :::{.example}
797     ## `lib.strings.hasInfix` usage example
799     ```nix
800     hasInfix "bc" "abcd"
801     => true
802     hasInfix "ab" "abcd"
803     => true
804     hasInfix "cd" "abcd"
805     => true
806     hasInfix "foo" "abcd"
807     => false
808     ```
810     :::
811   */
812   hasInfix = infix: content:
813     # Before 23.05, paths would be copied to the store before converting them
814     # to strings and comparing. This was surprising and confusing.
815     warnIf
816       (isPath infix)
817       ''
818         lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported.
819             There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
820             This function also copies the path to the Nix store, which may not be what you want.
821             This behavior is deprecated and will throw an error in the future.''
822       (builtins.match ".*${escapeRegex infix}.*" "${content}" != null);
824   /**
825     Convert a string `s` to a list of characters (i.e. singleton strings).
826     This allows you to, e.g., map a function over each character.  However,
827     note that this will likely be horribly inefficient; Nix is not a
828     general purpose programming language. Complex string manipulations
829     should, if appropriate, be done in a derivation.
830     Also note that Nix treats strings as a list of bytes and thus doesn't
831     handle unicode.
834     # Inputs
836     `s`
837     : 1\. Function argument
839     # Type
841     ```
842     stringToCharacters :: string -> [string]
843     ```
845     # Examples
846     :::{.example}
847     ## `lib.strings.stringToCharacters` usage example
849     ```nix
850     stringToCharacters ""
851     => [ ]
852     stringToCharacters "abc"
853     => [ "a" "b" "c" ]
854     stringToCharacters "🦄"
855     => [ "�" "�" "�" "�" ]
856     ```
858     :::
859   */
860   stringToCharacters = s:
861     genList (p: substring p 1 s) (stringLength s);
863   /**
864     Manipulate a string character by character and replace them by
865     strings before concatenating the results.
868     # Inputs
870     `f`
871     : Function to map over each individual character
873     `s`
874     : Input string
876     # Type
878     ```
879     stringAsChars :: (string -> string) -> string -> string
880     ```
882     # Examples
883     :::{.example}
884     ## `lib.strings.stringAsChars` usage example
886     ```nix
887     stringAsChars (x: if x == "a" then "i" else x) "nax"
888     => "nix"
889     ```
891     :::
892   */
893   stringAsChars =
894     # Function to map over each individual character
895     f:
896     # Input string
897     s: concatStrings (
898       map f (stringToCharacters s)
899     );
901   /**
902     Convert char to ascii value, must be in printable range
905     # Inputs
907     `c`
908     : 1\. Function argument
910     # Type
912     ```
913     charToInt :: string -> int
914     ```
916     # Examples
917     :::{.example}
918     ## `lib.strings.charToInt` usage example
920     ```nix
921     charToInt "A"
922     => 65
923     charToInt "("
924     => 40
925     ```
927     :::
928   */
929   charToInt = c: builtins.getAttr c asciiTable;
931   /**
932     Escape occurrence of the elements of `list` in `string` by
933     prefixing it with a backslash.
936     # Inputs
938     `list`
939     : 1\. Function argument
941     `string`
942     : 2\. Function argument
944     # Type
946     ```
947     escape :: [string] -> string -> string
948     ```
950     # Examples
951     :::{.example}
952     ## `lib.strings.escape` usage example
954     ```nix
955     escape ["(" ")"] "(foo)"
956     => "\\(foo\\)"
957     ```
959     :::
960   */
961   escape = list: replaceStrings list (map (c: "\\${c}") list);
963   /**
964     Escape occurrence of the element of `list` in `string` by
965     converting to its ASCII value and prefixing it with \\x.
966     Only works for printable ascii characters.
969     # Inputs
971     `list`
972     : 1\. Function argument
974     `string`
975     : 2\. Function argument
977     # Type
979     ```
980     escapeC = [string] -> string -> string
981     ```
983     # Examples
984     :::{.example}
985     ## `lib.strings.escapeC` usage example
987     ```nix
988     escapeC [" "] "foo bar"
989     => "foo\\x20bar"
990     ```
992     :::
993   */
994   escapeC = list: replaceStrings list (map (c: "\\x${ toLower (lib.toHexString (charToInt c))}") list);
996   /**
997     Escape the `string` so it can be safely placed inside a URL
998     query.
1000     # Inputs
1002     `string`
1003     : 1\. Function argument
1005     # Type
1007     ```
1008     escapeURL :: string -> string
1009     ```
1011     # Examples
1012     :::{.example}
1013     ## `lib.strings.escapeURL` usage example
1015     ```nix
1016     escapeURL "foo/bar baz"
1017     => "foo%2Fbar%20baz"
1018     ```
1020     :::
1021   */
1022   escapeURL = let
1023     unreserved = [ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "-" "_" "." "~" ];
1024     toEscape = builtins.removeAttrs asciiTable unreserved;
1025   in
1026     replaceStrings (builtins.attrNames toEscape) (lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") toEscape);
1028   /**
1029     Quote `string` to be used safely within the Bourne shell.
1032     # Inputs
1034     `string`
1035     : 1\. Function argument
1037     # Type
1039     ```
1040     escapeShellArg :: string -> string
1041     ```
1043     # Examples
1044     :::{.example}
1045     ## `lib.strings.escapeShellArg` usage example
1047     ```nix
1048     escapeShellArg "esc'ape\nme"
1049     => "'esc'\\''ape\nme'"
1050     ```
1052     :::
1053   */
1054   escapeShellArg = arg: "'${replaceStrings ["'"] ["'\\''"] (toString arg)}'";
1056   /**
1057     Quote all arguments to be safely passed to the Bourne shell.
1059     # Inputs
1061     `args`
1062     : 1\. Function argument
1064     # Type
1066     ```
1067     escapeShellArgs :: [string] -> string
1068     ```
1070     # Examples
1071     :::{.example}
1072     ## `lib.strings.escapeShellArgs` usage example
1074     ```nix
1075     escapeShellArgs ["one" "two three" "four'five"]
1076     => "'one' 'two three' 'four'\\''five'"
1077     ```
1079     :::
1080   */
1081   escapeShellArgs = concatMapStringsSep " " escapeShellArg;
1083   /**
1084     Test whether the given `name` is a valid POSIX shell variable name.
1087     # Inputs
1089     `name`
1090     : 1\. Function argument
1092     # Type
1094     ```
1095     string -> bool
1096     ```
1098     # Examples
1099     :::{.example}
1100     ## `lib.strings.isValidPosixName` usage example
1102     ```nix
1103     isValidPosixName "foo_bar000"
1104     => true
1105     isValidPosixName "0-bad.jpg"
1106     => false
1107     ```
1109     :::
1110   */
1111   isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
1113   /**
1114     Translate a Nix value into a shell variable declaration, with proper escaping.
1116     The value can be a string (mapped to a regular variable), a list of strings
1117     (mapped to a Bash-style array) or an attribute set of strings (mapped to a
1118     Bash-style associative array). Note that "string" includes string-coercible
1119     values like paths or derivations.
1121     Strings are translated into POSIX sh-compatible code; lists and attribute sets
1122     assume a shell that understands Bash syntax (e.g. Bash or ZSH).
1125     # Inputs
1127     `name`
1128     : 1\. Function argument
1130     `value`
1131     : 2\. Function argument
1133     # Type
1135     ```
1136     string -> ( string | [string] | { ${name} :: string; } ) -> string
1137     ```
1139     # Examples
1140     :::{.example}
1141     ## `lib.strings.toShellVar` usage example
1143     ```nix
1144     ''
1145       ${toShellVar "foo" "some string"}
1146       [[ "$foo" == "some string" ]]
1147     ''
1148     ```
1150     :::
1151   */
1152   toShellVar = name: value:
1153     lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
1154     if isAttrs value && ! isStringLike value then
1155       "declare -A ${name}=(${
1156         concatStringsSep " " (lib.mapAttrsToList (n: v:
1157           "[${escapeShellArg n}]=${escapeShellArg v}"
1158         ) value)
1159       })"
1160     else if isList value then
1161       "declare -a ${name}=(${escapeShellArgs value})"
1162     else
1163       "${name}=${escapeShellArg value}"
1164     );
1166   /**
1167     Translate an attribute set `vars` into corresponding shell variable declarations
1168     using `toShellVar`.
1171     # Inputs
1173     `vars`
1174     : 1\. Function argument
1176     # Type
1178     ```
1179     toShellVars :: {
1180       ${name} :: string | [ string ] | { ${key} :: string; };
1181     } -> string
1182     ```
1184     # Examples
1185     :::{.example}
1186     ## `lib.strings.toShellVars` usage example
1188     ```nix
1189     let
1190       foo = "value";
1191       bar = foo;
1192     in ''
1193       ${toShellVars { inherit foo bar; }}
1194       [[ "$foo" == "$bar" ]]
1195     ''
1196     ```
1198     :::
1199   */
1200   toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
1202   /**
1203     Turn a string `s` into a Nix expression representing that string
1205     # Inputs
1207     `s`
1208     : 1\. Function argument
1210     # Type
1212     ```
1213     escapeNixString :: string -> string
1214     ```
1216     # Examples
1217     :::{.example}
1218     ## `lib.strings.escapeNixString` usage example
1220     ```nix
1221     escapeNixString "hello\${}\n"
1222     => "\"hello\\\${}\\n\""
1223     ```
1225     :::
1226   */
1227   escapeNixString = s: escape ["$"] (toJSON s);
1229   /**
1230     Turn a string `s` into an exact regular expression
1232     # Inputs
1234     `s`
1235     : 1\. Function argument
1237     # Type
1239     ```
1240     escapeRegex :: string -> string
1241     ```
1243     # Examples
1244     :::{.example}
1245     ## `lib.strings.escapeRegex` usage example
1247     ```nix
1248     escapeRegex "[^a-z]*"
1249     => "\\[\\^a-z]\\*"
1250     ```
1252     :::
1253   */
1254   escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
1256   /**
1257     Quotes a string `s` if it can't be used as an identifier directly.
1260     # Inputs
1262     `s`
1263     : 1\. Function argument
1265     # Type
1267     ```
1268     escapeNixIdentifier :: string -> string
1269     ```
1271     # Examples
1272     :::{.example}
1273     ## `lib.strings.escapeNixIdentifier` usage example
1275     ```nix
1276     escapeNixIdentifier "hello"
1277     => "hello"
1278     escapeNixIdentifier "0abc"
1279     => "\"0abc\""
1280     ```
1282     :::
1283   */
1284   escapeNixIdentifier = s:
1285     # Regex from https://github.com/NixOS/nix/blob/d048577909e383439c2549e849c5c2f2016c997e/src/libexpr/lexer.l#L91
1286     if match "[a-zA-Z_][a-zA-Z0-9_'-]*" s != null
1287     then s else escapeNixString s;
1289   /**
1290     Escapes a string `s` such that it is safe to include verbatim in an XML
1291     document.
1293     # Inputs
1295     `s`
1296     : 1\. Function argument
1298     # Type
1300     ```
1301     escapeXML :: string -> string
1302     ```
1304     # Examples
1305     :::{.example}
1306     ## `lib.strings.escapeXML` usage example
1308     ```nix
1309     escapeXML ''"test" 'test' < & >''
1310     => "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;"
1311     ```
1313     :::
1314   */
1315   escapeXML = builtins.replaceStrings
1316     ["\"" "'" "<" ">" "&"]
1317     ["&quot;" "&apos;" "&lt;" "&gt;" "&amp;"];
1319   # warning added 12-12-2022
1320   replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
1322   # Case conversion utilities.
1323   lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
1324   upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1326   /**
1327     Converts an ASCII string `s` to lower-case.
1329     # Inputs
1331     `s`
1332     : The string to convert to lower-case.
1334     # Type
1336     ```
1337     toLower :: string -> string
1338     ```
1340     # Examples
1341     :::{.example}
1342     ## `lib.strings.toLower` usage example
1344     ```nix
1345     toLower "HOME"
1346     => "home"
1347     ```
1349     :::
1350   */
1351   toLower = replaceStrings upperChars lowerChars;
1353   /**
1354     Converts an ASCII string `s` to upper-case.
1356     # Inputs
1358     `s`
1359     : The string to convert to upper-case.
1362     # Type
1364     ```
1365     toUpper :: string -> string
1366     ```
1368     # Examples
1369     :::{.example}
1370     ## `lib.strings.toUpper` usage example
1372     ```nix
1373     toUpper "home"
1374     => "HOME"
1375     ```
1377     :::
1378   */
1379   toUpper = replaceStrings lowerChars upperChars;
1381   /**
1382     Appends string context from string like object `src` to `target`.
1384     :::{.warning}
1385     This is an implementation
1386     detail of Nix and should be used carefully.
1387     :::
1389     Strings in Nix carry an invisible `context` which is a list of strings
1390     representing store paths. If the string is later used in a derivation
1391     attribute, the derivation will properly populate the inputDrvs and
1392     inputSrcs.
1395     # Inputs
1397     `src`
1398     : The string to take the context from. If the argument is not a string,
1399       it will be implicitly converted to a string.
1401     `target`
1402     : The string to append the context to. If the argument is not a string,
1403       it will be implicitly converted to a string.
1405     # Type
1407     ```
1408     addContextFrom :: string -> string -> string
1409     ```
1411     # Examples
1412     :::{.example}
1413     ## `lib.strings.addContextFrom` usage example
1415     ```nix
1416     pkgs = import <nixpkgs> { };
1417     addContextFrom pkgs.coreutils "bar"
1418     => "bar"
1419     ```
1421     The context can be displayed using the `toString` function:
1423     ```nix
1424     nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
1425     {
1426       "/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
1427     }
1428     ```
1430     :::
1431   */
1432   addContextFrom = src: target: substring 0 0 src + target;
1434   /**
1435     Cut a string with a separator and produces a list of strings which
1436     were separated by this separator.
1438     # Inputs
1440     `sep`
1441     : 1\. Function argument
1443     `s`
1444     : 2\. Function argument
1446     # Type
1448     ```
1449     splitString :: string -> string -> [string]
1450     ```
1452     # Examples
1453     :::{.example}
1454     ## `lib.strings.splitString` usage example
1456     ```nix
1457     splitString "." "foo.bar.baz"
1458     => [ "foo" "bar" "baz" ]
1459     splitString "/" "/usr/local/bin"
1460     => [ "" "usr" "local" "bin" ]
1461     ```
1463     :::
1464   */
1465   splitString = sep: s:
1466     let
1467       splits = builtins.filter builtins.isString (builtins.split (escapeRegex (toString sep)) (toString s));
1468     in
1469       map (addContextFrom s) splits;
1472   /**
1473     Return a string without the specified prefix, if the prefix matches.
1475     # Inputs
1477     `prefix`
1478     : Prefix to remove if it matches
1480     `str`
1481     : Input string
1483     # Type
1485     ```
1486     removePrefix :: string -> string -> string
1487     ```
1489     # Examples
1490     :::{.example}
1491     ## `lib.strings.removePrefix` usage example
1493     ```nix
1494     removePrefix "foo." "foo.bar.baz"
1495     => "bar.baz"
1496     removePrefix "xxx" "foo.bar.baz"
1497     => "foo.bar.baz"
1498     ```
1500     :::
1501   */
1502   removePrefix =
1503     prefix:
1504     str:
1505     # Before 23.05, paths would be copied to the store before converting them
1506     # to strings and comparing. This was surprising and confusing.
1507     warnIf
1508       (isPath prefix)
1509       ''
1510         lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported.
1511             There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case.
1512             This function also copies the path to the Nix store, which may not be what you want.
1513             This behavior is deprecated and will throw an error in the future.''
1514     (let
1515       preLen = stringLength prefix;
1516     in
1517       if substring 0 preLen str == prefix then
1518         # -1 will take the string until the end
1519         substring preLen (-1) str
1520       else
1521         str);
1523   /**
1524     Return a string without the specified suffix, if the suffix matches.
1527     # Inputs
1529     `suffix`
1530     : Suffix to remove if it matches
1532     `str`
1533     : Input string
1535     # Type
1537     ```
1538     removeSuffix :: string -> string -> string
1539     ```
1541     # Examples
1542     :::{.example}
1543     ## `lib.strings.removeSuffix` usage example
1545     ```nix
1546     removeSuffix "front" "homefront"
1547     => "home"
1548     removeSuffix "xxx" "homefront"
1549     => "homefront"
1550     ```
1552     :::
1553   */
1554   removeSuffix =
1555     suffix:
1556     str:
1557     # Before 23.05, paths would be copied to the store before converting them
1558     # to strings and comparing. This was surprising and confusing.
1559     warnIf
1560       (isPath suffix)
1561       ''
1562         lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
1563             There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case.
1564             This function also copies the path to the Nix store, which may not be what you want.
1565             This behavior is deprecated and will throw an error in the future.''
1566     (let
1567       sufLen = stringLength suffix;
1568       sLen = stringLength str;
1569     in
1570       if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then
1571         substring 0 (sLen - sufLen) str
1572       else
1573         str);
1575   /**
1576     Return true if string `v1` denotes a version older than `v2`.
1579     # Inputs
1581     `v1`
1582     : 1\. Function argument
1584     `v2`
1585     : 2\. Function argument
1587     # Type
1589     ```
1590     versionOlder :: String -> String -> Bool
1591     ```
1593     # Examples
1594     :::{.example}
1595     ## `lib.strings.versionOlder` usage example
1597     ```nix
1598     versionOlder "1.1" "1.2"
1599     => true
1600     versionOlder "1.1" "1.1"
1601     => false
1602     ```
1604     :::
1605   */
1606   versionOlder = v1: v2: compareVersions v2 v1 == 1;
1608   /**
1609     Return true if string v1 denotes a version equal to or newer than v2.
1612     # Inputs
1614     `v1`
1615     : 1\. Function argument
1617     `v2`
1618     : 2\. Function argument
1620     # Type
1622     ```
1623     versionAtLeast :: String -> String -> Bool
1624     ```
1626     # Examples
1627     :::{.example}
1628     ## `lib.strings.versionAtLeast` usage example
1630     ```nix
1631     versionAtLeast "1.1" "1.0"
1632     => true
1633     versionAtLeast "1.1" "1.1"
1634     => true
1635     versionAtLeast "1.1" "1.2"
1636     => false
1637     ```
1639     :::
1640   */
1641   versionAtLeast = v1: v2: !versionOlder v1 v2;
1643   /**
1644     This function takes an argument `x` that's either a derivation or a
1645     derivation's "name" attribute and extracts the name part from that
1646     argument.
1648     # Inputs
1650     `x`
1651     : 1\. Function argument
1653     # Type
1655     ```
1656     getName :: String | Derivation -> String
1657     ```
1660     # Examples
1661     :::{.example}
1662     ## `lib.strings.getName` usage example
1664     ```nix
1665     getName "youtube-dl-2016.01.01"
1666     => "youtube-dl"
1667     getName pkgs.youtube-dl
1668     => "youtube-dl"
1669     ```
1671     :::
1672   */
1673   getName = let
1674     parse = drv: (parseDrvName drv).name;
1675   in x:
1676     if isString x
1677     then parse x
1678     else x.pname or (parse x.name);
1680   /**
1681     This function takes an argument `x` that's either a derivation or a
1682     derivation's "name" attribute and extracts the version part from that
1683     argument.
1686     # Inputs
1688     `x`
1689     : 1\. Function argument
1691     # Type
1693     ```
1694     getVersion :: String | Derivation -> String
1695     ```
1697     # Examples
1698     :::{.example}
1699     ## `lib.strings.getVersion` usage example
1701     ```nix
1702     getVersion "youtube-dl-2016.01.01"
1703     => "2016.01.01"
1704     getVersion pkgs.youtube-dl
1705     => "2016.01.01"
1706     ```
1708     :::
1709   */
1710   getVersion = let
1711     parse = drv: (parseDrvName drv).version;
1712   in x:
1713     if isString x
1714     then parse x
1715     else x.version or (parse x.name);
1717   /**
1718     Extract name and version from a URL as shown in the examples.
1720     Separator `sep` is used to determine the end of the extension.
1723     # Inputs
1725     `url`
1726     : 1\. Function argument
1728     `sep`
1729     : 2\. Function argument
1731     # Type
1733     ```
1734     nameFromURL :: String -> String
1735     ```
1737     # Examples
1738     :::{.example}
1739     ## `lib.strings.nameFromURL` usage example
1741     ```nix
1742     nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
1743     => "nix"
1744     nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
1745     => "nix-1.7-x86"
1746     ```
1748     :::
1749   */
1750   nameFromURL = url: sep:
1751     let
1752       components = splitString "/" url;
1753       filename = lib.last components;
1754       name = head (splitString sep filename);
1755     in assert name != filename; name;
1757   /**
1758     Create a `"-D<feature>:<type>=<value>"` string that can be passed to typical
1759     CMake invocations.
1761     # Inputs
1763     `feature`
1764     : The feature to be set
1766     `type`
1767     : The type of the feature to be set, as described in
1768       https://cmake.org/cmake/help/latest/command/set.html
1769       the possible values (case insensitive) are:
1770       BOOL FILEPATH PATH STRING INTERNAL
1772     `value`
1773     : The desired value
1775     # Type
1777     ```
1778     cmakeOptionType :: string -> string -> string -> string
1779     ```
1781     # Examples
1782     :::{.example}
1783     ## `lib.strings.cmakeOptionType` usage example
1785     ```nix
1786     cmakeOptionType "string" "ENGINE" "sdl2"
1787     => "-DENGINE:STRING=sdl2"
1788     ```
1790     :::
1791   */
1792   cmakeOptionType = let
1793     types = [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ];
1794   in type: feature: value:
1795     assert (elem (toUpper type) types);
1796     assert (isString feature);
1797     assert (isString value);
1798     "-D${feature}:${toUpper type}=${value}";
1800   /**
1801     Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
1802     CMake invocations.
1805     # Inputs
1807     `condition`
1808     : The condition to be made true or false
1810     `flag`
1811     : The controlling flag of the condition
1813     # Type
1815     ```
1816     cmakeBool :: string -> bool -> string
1817     ```
1819     # Examples
1820     :::{.example}
1821     ## `lib.strings.cmakeBool` usage example
1823     ```nix
1824     cmakeBool "ENABLE_STATIC_LIBS" false
1825     => "-DENABLESTATIC_LIBS:BOOL=FALSE"
1826     ```
1828     :::
1829   */
1830   cmakeBool = condition: flag:
1831     assert (lib.isString condition);
1832     assert (lib.isBool flag);
1833     cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag));
1835   /**
1836     Create a -D<feature>:STRING=<value> string that can be passed to typical
1837     CMake invocations.
1838     This is the most typical usage, so it deserves a special case.
1841     # Inputs
1843     `feature`
1844     : The feature to be set
1846     `value`
1847     : The desired value
1850     # Type
1852     ```
1853     cmakeFeature :: string -> string -> string
1854     ```
1856     # Examples
1857     :::{.example}
1858     ## `lib.strings.cmakeFeature` usage example
1860     ```nix
1861     cmakeFeature "MODULES" "badblock"
1862     => "-DMODULES:STRING=badblock"
1863     ```
1865     :::
1866   */
1867   cmakeFeature = feature: value:
1868     assert (lib.isString feature);
1869     assert (lib.isString value);
1870     cmakeOptionType "string" feature value;
1872   /**
1873     Create a -D<feature>=<value> string that can be passed to typical Meson
1874     invocations.
1877     # Inputs
1879     `feature`
1880     : The feature to be set
1882     `value`
1883     : The desired value
1885     # Type
1887     ```
1888     mesonOption :: string -> string -> string
1889     ```
1891     # Examples
1892     :::{.example}
1893     ## `lib.strings.mesonOption` usage example
1895     ```nix
1896     mesonOption "engine" "opengl"
1897     => "-Dengine=opengl"
1898     ```
1900     :::
1901   */
1902   mesonOption = feature: value:
1903     assert (lib.isString feature);
1904     assert (lib.isString value);
1905     "-D${feature}=${value}";
1907   /**
1908     Create a -D<condition>={true,false} string that can be passed to typical
1909     Meson invocations.
1912     # Inputs
1914     `condition`
1915     : The condition to be made true or false
1917     `flag`
1918     : The controlling flag of the condition
1920     # Type
1922     ```
1923     mesonBool :: string -> bool -> string
1924     ```
1926     # Examples
1927     :::{.example}
1928     ## `lib.strings.mesonBool` usage example
1930     ```nix
1931     mesonBool "hardened" true
1932     => "-Dhardened=true"
1933     mesonBool "static" false
1934     => "-Dstatic=false"
1935     ```
1937     :::
1938   */
1939   mesonBool = condition: flag:
1940     assert (lib.isString condition);
1941     assert (lib.isBool flag);
1942     mesonOption condition (lib.boolToString flag);
1944   /**
1945     Create a -D<feature>={enabled,disabled} string that can be passed to
1946     typical Meson invocations.
1949     # Inputs
1951     `feature`
1952     : The feature to be enabled or disabled
1954     `flag`
1955     : The controlling flag
1957     # Type
1959     ```
1960     mesonEnable :: string -> bool -> string
1961     ```
1963     # Examples
1964     :::{.example}
1965     ## `lib.strings.mesonEnable` usage example
1967     ```nix
1968     mesonEnable "docs" true
1969     => "-Ddocs=enabled"
1970     mesonEnable "savage" false
1971     => "-Dsavage=disabled"
1972     ```
1974     :::
1975   */
1976   mesonEnable = feature: flag:
1977     assert (lib.isString feature);
1978     assert (lib.isBool flag);
1979     mesonOption feature (if flag then "enabled" else "disabled");
1981   /**
1982     Create an --{enable,disable}-<feature> string that can be passed to
1983     standard GNU Autoconf scripts.
1986     # Inputs
1988     `flag`
1989     : 1\. Function argument
1991     `feature`
1992     : 2\. Function argument
1994     # Type
1996     ```
1997     enableFeature :: bool -> string -> string
1998     ```
2000     # Examples
2001     :::{.example}
2002     ## `lib.strings.enableFeature` usage example
2004     ```nix
2005     enableFeature true "shared"
2006     => "--enable-shared"
2007     enableFeature false "shared"
2008     => "--disable-shared"
2009     ```
2011     :::
2012   */
2013   enableFeature = flag: feature:
2014     assert lib.isBool flag;
2015     assert lib.isString feature; # e.g. passing openssl instead of "openssl"
2016     "--${if flag then "enable" else "disable"}-${feature}";
2018   /**
2019     Create an --{enable-<feature>=<value>,disable-<feature>} string that can be passed to
2020     standard GNU Autoconf scripts.
2023     # Inputs
2025     `flag`
2026     : 1\. Function argument
2028     `feature`
2029     : 2\. Function argument
2031     `value`
2032     : 3\. Function argument
2034     # Type
2036     ```
2037     enableFeatureAs :: bool -> string -> string -> string
2038     ```
2040     # Examples
2041     :::{.example}
2042     ## `lib.strings.enableFeatureAs` usage example
2044     ```nix
2045     enableFeatureAs true "shared" "foo"
2046     => "--enable-shared=foo"
2047     enableFeatureAs false "shared" (throw "ignored")
2048     => "--disable-shared"
2049     ```
2051     :::
2052   */
2053   enableFeatureAs = flag: feature: value:
2054     enableFeature flag feature + optionalString flag "=${value}";
2056   /**
2057     Create an --{with,without}-<feature> string that can be passed to
2058     standard GNU Autoconf scripts.
2061     # Inputs
2063     `flag`
2064     : 1\. Function argument
2066     `feature`
2067     : 2\. Function argument
2070     # Type
2072     ```
2073     withFeature :: bool -> string -> string
2074     ```
2076     # Examples
2077     :::{.example}
2078     ## `lib.strings.withFeature` usage example
2080     ```nix
2081     withFeature true "shared"
2082     => "--with-shared"
2083     withFeature false "shared"
2084     => "--without-shared"
2085     ```
2087     :::
2088   */
2089   withFeature = flag: feature:
2090     assert isString feature; # e.g. passing openssl instead of "openssl"
2091     "--${if flag then "with" else "without"}-${feature}";
2093   /**
2094     Create an --{with-<feature>=<value>,without-<feature>} string that can be passed to
2095     standard GNU Autoconf scripts.
2098     # Inputs
2100     `flag`
2101     : 1\. Function argument
2103     `feature`
2104     : 2\. Function argument
2106     `value`
2107     : 3\. Function argument
2109     # Type
2111     ```
2112     withFeatureAs :: bool -> string -> string -> string
2113     ```
2116     # Examples
2117     :::{.example}
2118     ## `lib.strings.withFeatureAs` usage example
2120     ```nix
2121     withFeatureAs true "shared" "foo"
2122     => "--with-shared=foo"
2123     withFeatureAs false "shared" (throw "ignored")
2124     => "--without-shared"
2125     ```
2127     :::
2128   */
2129   withFeatureAs = flag: feature: value:
2130     withFeature flag feature + optionalString flag "=${value}";
2132   /**
2133     Create a fixed width string with additional prefix to match
2134     required width.
2136     This function will fail if the input string is longer than the
2137     requested length.
2140     # Inputs
2142     `width`
2143     : 1\. Function argument
2145     `filler`
2146     : 2\. Function argument
2148     `str`
2149     : 3\. Function argument
2151     # Type
2153     ```
2154     fixedWidthString :: int -> string -> string -> string
2155     ```
2157     # Examples
2158     :::{.example}
2159     ## `lib.strings.fixedWidthString` usage example
2161     ```nix
2162     fixedWidthString 5 "0" (toString 15)
2163     => "00015"
2164     ```
2166     :::
2167   */
2168   fixedWidthString = width: filler: str:
2169     let
2170       strw = lib.stringLength str;
2171       reqWidth = width - (lib.stringLength filler);
2172     in
2173       assert lib.assertMsg (strw <= width)
2174         "fixedWidthString: requested string length (${
2175           toString width}) must not be shorter than actual length (${
2176             toString strw})";
2177       if strw == width then str else filler + fixedWidthString reqWidth filler str;
2179   /**
2180     Format a number adding leading zeroes up to fixed width.
2183     # Inputs
2185     `width`
2186     : 1\. Function argument
2188     `n`
2189     : 2\. Function argument
2191     # Type
2193     ```
2194     fixedWidthNumber :: int -> int -> string
2195     ```
2197     # Examples
2198     :::{.example}
2199     ## `lib.strings.fixedWidthNumber` usage example
2201     ```nix
2202     fixedWidthNumber 5 15
2203     => "00015"
2204     ```
2206     :::
2207   */
2208   fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
2210   /**
2211     Convert a float to a string, but emit a warning when precision is lost
2212     during the conversion
2215     # Inputs
2217     `float`
2218     : 1\. Function argument
2221     # Type
2223     ```
2224     floatToString :: float -> string
2225     ```
2227     # Examples
2228     :::{.example}
2229     ## `lib.strings.floatToString` usage example
2231     ```nix
2232     floatToString 0.000001
2233     => "0.000001"
2234     floatToString 0.0000001
2235     => trace: warning: Imprecise conversion from float to string 0.000000
2236        "0.000000"
2237     ```
2239     :::
2240   */
2241   floatToString = float: let
2242     result = toString float;
2243     precise = float == fromJSON result;
2244   in lib.warnIf (!precise) "Imprecise conversion from float to string ${result}"
2245     result;
2247   /**
2248     Check whether a value `val` can be coerced to a string.
2250     :::{.warning}
2251     Soft-deprecated function. While the original implementation is available as
2252     `isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
2253     :::
2255     # Inputs
2257     `val`
2258     : 1\. Function argument
2260     # Type
2262     ```
2263     isCoercibleToString :: a -> bool
2264     ```
2265   */
2266   isCoercibleToString = lib.warnIf (lib.isInOldestRelease 2305)
2267     "lib.strings.isCoercibleToString is deprecated in favor of either isStringLike or isConvertibleWithToString. Only use the latter if it needs to return true for null, numbers, booleans and list of similarly coercibles."
2268     isConvertibleWithToString;
2270   /**
2271     Check whether a list or other value `x` can be passed to toString.
2273     Many types of value are coercible to string this way, including `int`, `float`,
2274     `null`, `bool`, `list` of similarly coercible values.
2276     # Inputs
2278     `val`
2279     : 1\. Function argument
2281     # Type
2283     ```
2284     isConvertibleWithToString :: a -> bool
2285     ```
2286   */
2287   isConvertibleWithToString = let
2288     types = [ "null" "int" "float" "bool" ];
2289   in x:
2290     isStringLike x ||
2291     elem (typeOf x) types ||
2292     (isList x && lib.all isConvertibleWithToString x);
2294   /**
2295     Check whether a value can be coerced to a string.
2296     The value must be a string, path, or attribute set.
2298     String-like values can be used without explicit conversion in
2299     string interpolations and in most functions that expect a string.
2302     # Inputs
2304     `x`
2305     : 1\. Function argument
2307     # Type
2309     ```
2310     isStringLike :: a -> bool
2311     ```
2312   */
2313   isStringLike = x:
2314     isString x ||
2315     isPath x ||
2316     x ? outPath ||
2317     x ? __toString;
2319   /**
2320     Check whether a value `x` is a store path.
2323     # Inputs
2325     `x`
2326     : 1\. Function argument
2328     # Type
2330     ```
2331     isStorePath :: a -> bool
2332     ```
2334     # Examples
2335     :::{.example}
2336     ## `lib.strings.isStorePath` usage example
2338     ```nix
2339     isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
2340     => false
2341     isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"
2342     => true
2343     isStorePath pkgs.python
2344     => true
2345     isStorePath [] || isStorePath 42 || isStorePath {} || â€¦
2346     => false
2347     ```
2349     :::
2350   */
2351   isStorePath = x:
2352     if isStringLike x then
2353       let str = toString x; in
2354       substring 0 1 str == "/"
2355       && dirOf str == storeDir
2356     else
2357       false;
2359   /**
2360     Parse a string as an int. Does not support parsing of integers with preceding zero due to
2361     ambiguity between zero-padded and octal numbers. See toIntBase10.
2363     # Inputs
2365     `str`
2366     : A string to be interpreted as an int.
2368     # Type
2370     ```
2371     toInt :: string -> int
2372     ```
2374     # Examples
2375     :::{.example}
2376     ## `lib.strings.toInt` usage example
2378     ```nix
2379     toInt "1337"
2380     => 1337
2382     toInt "-4"
2383     => -4
2385     toInt " 123 "
2386     => 123
2388     toInt "00024"
2389     => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
2391     toInt "3.14"
2392     => error: floating point JSON numbers are not supported
2393     ```
2395     :::
2396   */
2397   toInt =
2398     let
2399       matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*";
2400       matchLeadingZero = match "0[[:digit:]]+";
2401     in
2402     str:
2403     let
2404       # RegEx: Match any leading whitespace, possibly a '-', one or more digits,
2405       # and finally match any trailing whitespace.
2406       strippedInput = matchStripInput str;
2408       # RegEx: Match a leading '0' then one or more digits.
2409       isLeadingZero = matchLeadingZero (head strippedInput) == [];
2411       # Attempt to parse input
2412       parsedInput = fromJSON (head strippedInput);
2414       generalError = "toInt: Could not convert ${escapeNixString str} to int.";
2416     in
2417       # Error on presence of non digit characters.
2418       if strippedInput == null
2419       then throw generalError
2420       # Error on presence of leading zero/octal ambiguity.
2421       else if isLeadingZero
2422       then throw "toInt: Ambiguity in interpretation of ${escapeNixString str} between octal and zero padded integer."
2423       # Error if parse function fails.
2424       else if !isInt parsedInput
2425       then throw generalError
2426       # Return result.
2427       else parsedInput;
2430   /**
2431     Parse a string as a base 10 int. This supports parsing of zero-padded integers.
2433     # Inputs
2435     `str`
2436     : A string to be interpreted as an int.
2438     # Type
2440     ```
2441     toIntBase10 :: string -> int
2442     ```
2444     # Examples
2445     :::{.example}
2446     ## `lib.strings.toIntBase10` usage example
2448     ```nix
2449     toIntBase10 "1337"
2450     => 1337
2452     toIntBase10 "-4"
2453     => -4
2455     toIntBase10 " 123 "
2456     => 123
2458     toIntBase10 "00024"
2459     => 24
2461     toIntBase10 "3.14"
2462     => error: floating point JSON numbers are not supported
2463     ```
2465     :::
2466   */
2467   toIntBase10 =
2468     let
2469       matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*";
2470       matchZero = match "0+";
2471     in
2472     str:
2473     let
2474       # RegEx: Match any leading whitespace, then match any zero padding,
2475       # capture possibly a '-' followed by one or more digits,
2476       # and finally match any trailing whitespace.
2477       strippedInput = matchStripInput str;
2479       # RegEx: Match at least one '0'.
2480       isZero = matchZero (head strippedInput) == [];
2482       # Attempt to parse input
2483       parsedInput = fromJSON (head strippedInput);
2485       generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";
2487     in
2488       # Error on presence of non digit characters.
2489       if strippedInput == null
2490       then throw generalError
2491       # In the special case zero-padded zero (00000), return early.
2492       else if isZero
2493       then 0
2494       # Error if parse function fails.
2495       else if !isInt parsedInput
2496       then throw generalError
2497       # Return result.
2498       else parsedInput;
2500   /**
2501     Read a list of paths from `file`, relative to the `rootPath`.
2502     Lines beginning with `#` are treated as comments and ignored.
2503     Whitespace is significant.
2505     :::{.warning}
2506     This function is deprecated and should be avoided.
2507     :::
2509     :::{.note}
2510     This function is not performant and should be avoided.
2511     :::
2513     # Inputs
2515     `rootPath`
2516     : 1\. Function argument
2518     `file`
2519     : 2\. Function argument
2521     # Type
2523     ```
2524     readPathsFromFile :: string -> string -> [string]
2525     ```
2527     # Examples
2528     :::{.example}
2529     ## `lib.strings.readPathsFromFile` usage example
2531     ```nix
2532     readPathsFromFile /prefix
2533       ./pkgs/development/libraries/qt-5/5.4/qtbase/series
2534     => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
2535          "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
2536          "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
2537          "/prefix/nix-profiles-library-paths.patch"
2538          "/prefix/compose-search-path.patch" ]
2539     ```
2541     :::
2542   */
2543   readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead."
2544     (rootPath: file:
2545       let
2546         lines = lib.splitString "\n" (readFile file);
2547         removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line));
2548         relativePaths = removeComments lines;
2549         absolutePaths = map (path: rootPath + "/${path}") relativePaths;
2550       in
2551         absolutePaths);
2553   /**
2554     Read the contents of a file removing the trailing \n
2557     # Inputs
2559     `file`
2560     : 1\. Function argument
2562     # Type
2564     ```
2565     fileContents :: path -> string
2566     ```
2568     # Examples
2569     :::{.example}
2570     ## `lib.strings.fileContents` usage example
2572     ```nix
2573     $ echo "1.0" > ./version
2575     fileContents ./version
2576     => "1.0"
2577     ```
2579     :::
2580   */
2581   fileContents = file: removeSuffix "\n" (readFile file);
2584   /**
2585     Creates a valid derivation name from a potentially invalid one.
2587     # Inputs
2589     `string`
2590     : 1\. Function argument
2592     # Type
2594     ```
2595     sanitizeDerivationName :: String -> String
2596     ```
2598     # Examples
2599     :::{.example}
2600     ## `lib.strings.sanitizeDerivationName` usage example
2602     ```nix
2603     sanitizeDerivationName "../hello.bar # foo"
2604     => "-hello.bar-foo"
2605     sanitizeDerivationName ""
2606     => "unknown"
2607     sanitizeDerivationName pkgs.hello
2608     => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10"
2609     ```
2611     :::
2612   */
2613   sanitizeDerivationName =
2614   let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*";
2615   in
2616   string:
2617   # First detect the common case of already valid strings, to speed those up
2618   if stringLength string <= 207 && okRegex string != null
2619   then unsafeDiscardStringContext string
2620   else lib.pipe string [
2621     # Get rid of string context. This is safe under the assumption that the
2622     # resulting string is only used as a derivation name
2623     unsafeDiscardStringContext
2624     # Strip all leading "."
2625     (x: elemAt (match "\\.*(.*)" x) 0)
2626     # Split out all invalid characters
2627     # https://github.com/NixOS/nix/blob/2.3.2/src/libstore/store-api.cc#L85-L112
2628     # https://github.com/NixOS/nix/blob/2242be83c61788b9c0736a92bb0b5c7bbfc40803/nix-rust/src/store/path.rs#L100-L125
2629     (split "[^[:alnum:]+._?=-]+")
2630     # Replace invalid character ranges with a "-"
2631     (concatMapStrings (s: if lib.isList s then "-" else s))
2632     # Limit to 211 characters (minus 4 chars for ".drv")
2633     (x: substring (lib.max (stringLength x - 207) 0) (-1) x)
2634     # If the result is empty, replace it with "unknown"
2635     (x: if stringLength x == 0 then "unknown" else x)
2636   ];
2638   /**
2639     Computes the Levenshtein distance between two strings `a` and `b`.
2641     Complexity O(n*m) where n and m are the lengths of the strings.
2642     Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742
2645     # Inputs
2647     `a`
2648     : 1\. Function argument
2650     `b`
2651     : 2\. Function argument
2653     # Type
2655     ```
2656     levenshtein :: string -> string -> int
2657     ```
2659     # Examples
2660     :::{.example}
2661     ## `lib.strings.levenshtein` usage example
2663     ```nix
2664     levenshtein "foo" "foo"
2665     => 0
2666     levenshtein "book" "hook"
2667     => 1
2668     levenshtein "hello" "Heyo"
2669     => 3
2670     ```
2672     :::
2673   */
2674   levenshtein = a: b: let
2675     # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1)
2676     arr = lib.genList (i:
2677       lib.genList (j:
2678         dist i j
2679       ) (stringLength b + 1)
2680     ) (stringLength a + 1);
2681     d = x: y: lib.elemAt (lib.elemAt arr x) y;
2682     dist = i: j:
2683       let c = if substring (i - 1) 1 a == substring (j - 1) 1 b
2684         then 0 else 1;
2685       in
2686       if j == 0 then i
2687       else if i == 0 then j
2688       else lib.min
2689         ( lib.min (d (i - 1) j + 1) (d i (j - 1) + 1))
2690         ( d (i - 1) (j - 1) + c );
2691   in d (stringLength a) (stringLength b);
2693   /**
2694     Returns the length of the prefix that appears in both strings `a` and `b`.
2697     # Inputs
2699     `a`
2700     : 1\. Function argument
2702     `b`
2703     : 2\. Function argument
2705     # Type
2707     ```
2708     commonPrefixLength :: string -> string -> int
2709     ```
2710   */
2711   commonPrefixLength = a: b:
2712     let
2713       m = lib.min (stringLength a) (stringLength b);
2714       go = i: if i >= m then m else if substring i 1 a == substring i 1 b then go (i + 1) else i;
2715     in go 0;
2717   /**
2718     Returns the length of the suffix common to both strings `a` and `b`.
2721     # Inputs
2723     `a`
2724     : 1\. Function argument
2726     `b`
2727     : 2\. Function argument
2729     # Type
2731     ```
2732     commonSuffixLength :: string -> string -> int
2733     ```
2734   */
2735   commonSuffixLength = a: b:
2736     let
2737       m = lib.min (stringLength a) (stringLength b);
2738       go = i: if i >= m then m else if substring (stringLength a - i - 1) 1 a == substring (stringLength b - i - 1) 1 b then go (i + 1) else i;
2739     in go 0;
2741   /**
2742     Returns whether the levenshtein distance between two strings `a` and `b` is at most some value `k`.
2744     Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise
2746     # Inputs
2748     `k`
2749     : Distance threshold
2751     `a`
2752     : String `a`
2754     `b`
2755     : String `b`
2757     # Type
2759     ```
2760     levenshteinAtMost :: int -> string -> string -> bool
2761     ```
2763     # Examples
2764     :::{.example}
2765     ## `lib.strings.levenshteinAtMost` usage example
2767     ```nix
2768     levenshteinAtMost 0 "foo" "foo"
2769     => true
2770     levenshteinAtMost 1 "foo" "boa"
2771     => false
2772     levenshteinAtMost 2 "foo" "boa"
2773     => true
2774     levenshteinAtMost 2 "This is a sentence" "this is a sentense."
2775     => false
2776     levenshteinAtMost 3 "This is a sentence" "this is a sentense."
2777     => true
2778     ```
2780     :::
2781   */
2782   levenshteinAtMost = let
2783     infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1;
2785     # This function takes two strings stripped by their common pre and suffix,
2786     # and returns whether they differ by at most two by Levenshtein distance.
2787     # Because of this stripping, if they do indeed differ by at most two edits,
2788     # we know that those edits were (if at all) done at the start or the end,
2789     # while the middle has to have stayed the same. This fact is used in the
2790     # implementation.
2791     infixDifferAtMost2 = x: y:
2792       let
2793         xlen = stringLength x;
2794         ylen = stringLength y;
2795         # This function is only called with |x| >= |y| and |x| - |y| <= 2, so
2796         # diff is one of 0, 1 or 2
2797         diff = xlen - ylen;
2799         # Infix of x and y, stripped by the left and right most character
2800         xinfix = substring 1 (xlen - 2) x;
2801         yinfix = substring 1 (ylen - 2) y;
2803         # x and y but a character deleted at the left or right
2804         xdelr = substring 0 (xlen - 1) x;
2805         xdell = substring 1 (xlen - 1) x;
2806         ydelr = substring 0 (ylen - 1) y;
2807         ydell = substring 1 (ylen - 1) y;
2808       in
2809         # A length difference of 2 can only be gotten with 2 delete edits,
2810         # which have to have happened at the start and end of x
2811         # Example: "abcdef" -> "bcde"
2812         if diff == 2 then xinfix == y
2813         # A length difference of 1 can only be gotten with a deletion on the
2814         # right and a replacement on the left or vice versa.
2815         # Example: "abcdef" -> "bcdez" or "zbcde"
2816         else if diff == 1 then xinfix == ydelr || xinfix == ydell
2817         # No length difference can either happen through replacements on both
2818         # sides, or a deletion on the left and an insertion on the right or
2819         # vice versa
2820         # Example: "abcdef" -> "zbcdez" or "bcdefz" or "zabcde"
2821         else xinfix == yinfix || xdelr == ydell || xdell == ydelr;
2823     in k: if k <= 0 then a: b: a == b else
2824       let f = a: b:
2825         let
2826           alen = stringLength a;
2827           blen = stringLength b;
2828           prelen = commonPrefixLength a b;
2829           suflen = commonSuffixLength a b;
2830           presuflen = prelen + suflen;
2831           ainfix = substring prelen (alen - presuflen) a;
2832           binfix = substring prelen (blen - presuflen) b;
2833         in
2834         # Make a be the bigger string
2835         if alen < blen then f b a
2836         # If a has over k more characters than b, even with k deletes on a, b can't be reached
2837         else if alen - blen > k then false
2838         else if k == 1 then infixDifferAtMost1 ainfix binfix
2839         else if k == 2 then infixDifferAtMost2 ainfix binfix
2840         else levenshtein ainfix binfix <= k;
2841       in f;