1/**
2 String manipulation functions.
3*/
4{ lib }:
5let
6
7 inherit (builtins) length;
8
9 inherit (lib.trivial) warnIf;
10
11asciiTable = import ./ascii-table.nix;
12
13in
14
15rec {
16
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 ;
43
44 /**
45 Concatenate a list of strings.
46
47 # Type
48
49 ```
50 concatStrings :: [string] -> string
51 ```
52
53 # Examples
54 :::{.example}
55 ## `lib.strings.concatStrings` usage example
56
57 ```nix
58 concatStrings ["foo" "bar"]
59 => "foobar"
60 ```
61
62 :::
63 */
64 concatStrings = builtins.concatStringsSep "";
65
66 /**
67 Map a function over a list and concatenate the resulting strings.
68
69
70 # Inputs
71
72 `f`
73 : 1\. Function argument
74
75 `list`
76 : 2\. Function argument
77
78 # Type
79
80 ```
81 concatMapStrings :: (a -> string) -> [a] -> string
82 ```
83
84 # Examples
85 :::{.example}
86 ## `lib.strings.concatMapStrings` usage example
87
88 ```nix
89 concatMapStrings (x: "a" + x) ["foo" "bar"]
90 => "afooabar"
91 ```
92
93 :::
94 */
95 concatMapStrings = f: list: concatStrings (map f list);
96
97 /**
98 Like `concatMapStrings` except that the f functions also gets the
99 position as a parameter.
100
101
102 # Inputs
103
104 `f`
105 : 1\. Function argument
106
107 `list`
108 : 2\. Function argument
109
110 # Type
111
112 ```
113 concatImapStrings :: (int -> a -> string) -> [a] -> string
114 ```
115
116 # Examples
117 :::{.example}
118 ## `lib.strings.concatImapStrings` usage example
119
120 ```nix
121 concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"]
122 => "1-foo2-bar"
123 ```
124
125 :::
126 */
127 concatImapStrings = f: list: concatStrings (lib.imap1 f list);
128
129 /**
130 Place an element between each element of a list
131
132
133 # Inputs
134
135 `separator`
136 : Separator to add between elements
137
138 `list`
139 : Input list
140
141 # Type
142
143 ```
144 intersperse :: a -> [a] -> [a]
145 ```
146
147 # Examples
148 :::{.example}
149 ## `lib.strings.intersperse` usage example
150
151 ```nix
152 intersperse "/" ["usr" "local" "bin"]
153 => ["usr" "/" "local" "/" "bin"].
154 ```
155
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);
164
165 /**
166 Concatenate a list of strings with a separator between each element
167
168 # Inputs
169
170 `sep`
171 : Separator to add between elements
172
173 `list`
174 : List of input strings
175
176 # Type
177
178 ```
179 concatStringsSep :: string -> [string] -> string
180 ```
181
182 # Examples
183 :::{.example}
184 ## `lib.strings.concatStringsSep` usage example
185
186 ```nix
187 concatStringsSep "/" ["usr" "local" "bin"]
188 => "usr/local/bin"
189 ```
190
191 :::
192 */
193 concatStringsSep = builtins.concatStringsSep;
194
195 /**
196 Maps a function over a list of strings and then concatenates the
197 result with the specified separator interspersed between
198 elements.
199
200
201 # Inputs
202
203 `sep`
204 : Separator to add between elements
205
206 `f`
207 : Function to map over the list
208
209 `list`
210 : List of input strings
211
212 # Type
213
214 ```
215 concatMapStringsSep :: string -> (a -> string) -> [a] -> string
216 ```
217
218 # Examples
219 :::{.example}
220 ## `lib.strings.concatMapStringsSep` usage example
221
222 ```nix
223 concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"]
224 => "FOO-BAR-BAZ"
225 ```
226
227 :::
228 */
229 concatMapStringsSep =
230 sep:
231 f:
232 list: concatStringsSep sep (map f list);
233
234 /**
235 Same as `concatMapStringsSep`, but the mapping function
236 additionally receives the position of its argument.
237
238
239 # Inputs
240
241 `sep`
242 : Separator to add between elements
243
244 `f`
245 : Function that receives elements and their positions
246
247 `list`
248 : List of input strings
249
250 # Type
251
252 ```
253 concatIMapStringsSep :: string -> (int -> a -> string) -> [a] -> string
254 ```
255
256 # Examples
257 :::{.example}
258 ## `lib.strings.concatImapStringsSep` usage example
259
260 ```nix
261 concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
262 => "6-3-2"
263 ```
264
265 :::
266 */
267 concatImapStringsSep =
268 sep:
269 f:
270 list: concatStringsSep sep (lib.imap1 f list);
271
272 /**
273 Like [`concatMapStringsSep`](#function-library-lib.strings.concatMapStringsSep)
274 but takes an attribute set instead of a list.
275
276 # Inputs
277
278 `sep`
279 : Separator to add between item strings
280
281 `f`
282 : Function that takes each key and value and return a string
283
284 `attrs`
285 : Attribute set to map from
286
287 # Type
288
289 ```
290 concatMapAttrsStringSep :: String -> (String -> Any -> String) -> AttrSet -> String
291 ```
292
293 # Examples
294
295 :::{.example}
296 ## `lib.strings.concatMapAttrsStringSep` usage example
297
298 ```nix
299 concatMapAttrsStringSep "\n" (name: value: "${name}: foo-${value}") { a = "0.1.0"; b = "0.2.0"; }
300 => "a: foo-0.1.0\nb: foo-0.2.0"
301 ```
302
303 :::
304 */
305 concatMapAttrsStringSep =
306 sep: f: attrs:
307 concatStringsSep sep (lib.attrValues (lib.mapAttrs f attrs));
308
309 /**
310 Concatenate a list of strings, adding a newline at the end of each one.
311 Defined as `concatMapStrings (s: s + "\n")`.
312
313 # Inputs
314
315 `list`
316 : List of strings. Any element that is not a string will be implicitly converted to a string.
317
318 # Type
319
320 ```
321 concatLines :: [string] -> string
322 ```
323
324 # Examples
325 :::{.example}
326 ## `lib.strings.concatLines` usage example
327
328 ```nix
329 concatLines [ "foo" "bar" ]
330 => "foo\nbar\n"
331 ```
332
333 :::
334 */
335 concatLines = concatMapStrings (s: s + "\n");
336
337 /**
338 Repeat a string `n` times,
339 and concatenate the parts into a new string.
340
341
342 # Inputs
343
344 `n`
345 : 1\. Function argument
346
347 `s`
348 : 2\. Function argument
349
350 # Type
351
352 ```
353 replicate :: int -> string -> string
354 ```
355
356 # Examples
357 :::{.example}
358 ## `lib.strings.replicate` usage example
359
360 ```nix
361 replicate 3 "v"
362 => "vvv"
363 replicate 5 "hello"
364 => "hellohellohellohellohello"
365 ```
366
367 :::
368 */
369 replicate = n: s: concatStrings (lib.lists.replicate n s);
370
371 /**
372 Remove leading and trailing whitespace from a string `s`.
373
374 Whitespace is defined as any of the following characters:
375 " ", "\t" "\r" "\n"
376
377 # Inputs
378
379 `s`
380 : The string to trim
381
382 # Type
383
384 ```
385 trim :: string -> string
386 ```
387
388 # Examples
389 :::{.example}
390 ## `lib.strings.trim` usage example
391
392 ```nix
393 trim " hello, world! "
394 => "hello, world!"
395 ```
396
397 :::
398 */
399 trim = trimWith {
400 start = true;
401 end = true;
402 };
403
404 /**
405 Remove leading and/or trailing whitespace from a string `s`.
406
407 To remove both leading and trailing whitespace, you can also use [`trim`](#function-library-lib.strings.trim)
408
409 Whitespace is defined as any of the following characters:
410 " ", "\t" "\r" "\n"
411
412 # Inputs
413
414 `config` (Attribute set)
415 : `start`
416 : Whether to trim leading whitespace (`false` by default)
417
418 : `end`
419 : Whether to trim trailing whitespace (`false` by default)
420
421 `s`
422 : The string to trim
423
424 # Type
425
426 ```
427 trimWith :: { start :: Bool; end :: Bool } -> String -> String
428 ```
429
430 # Examples
431 :::{.example}
432 ## `lib.strings.trimWith` usage example
433
434 ```nix
435 trimWith { start = true; } " hello, world! "}
436 => "hello, world! "
437
438 trimWith { end = true; } " hello, world! "}
439 => " hello, world!"
440 ```
441 :::
442 */
443 trimWith =
444 {
445 start ? false,
446 end ? false,
447 }:
448 let
449 # Define our own whitespace character class instead of using
450 # `[:space:]`, which is not well-defined.
451 chars = " \t\r\n";
452
453 # To match up until trailing whitespace, we need to capture a
454 # group that ends with a non-whitespace character.
455 regex =
456 if start && end then
457 "[${chars}]*(.*[^${chars}])[${chars}]*"
458 else if start then
459 "[${chars}]*(.*)"
460 else if end then
461 "(.*[^${chars}])[${chars}]*"
462 else
463 "(.*)";
464 in
465 s:
466 let
467 # If the string was empty or entirely whitespace,
468 # then the regex may not match and `res` will be `null`.
469 res = match regex s;
470 in
471 optionalString (res != null) (head res);
472
473 /**
474 Construct a Unix-style, colon-separated search path consisting of
475 the given `subDir` appended to each of the given paths.
476
477 # Inputs
478
479 `subDir`
480 : Directory name to append
481
482 `paths`
483 : List of base paths
484
485 # Type
486
487 ```
488 makeSearchPath :: string -> [string] -> string
489 ```
490
491 # Examples
492 :::{.example}
493 ## `lib.strings.makeSearchPath` usage example
494
495 ```nix
496 makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
497 => "/root/bin:/usr/bin:/usr/local/bin"
498 makeSearchPath "bin" [""]
499 => "/bin"
500 ```
501
502 :::
503 */
504 makeSearchPath =
505 subDir:
506 paths:
507 concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths));
508
509 /**
510 Construct a Unix-style search path by appending the given
511 `subDir` to the specified `output` of each of the packages.
512
513 If no output by the given name is found, fallback to `.out` and then to
514 the default.
515
516
517 # Inputs
518
519 `output`
520 : Package output to use
521
522 `subDir`
523 : Directory name to append
524
525 `pkgs`
526 : List of packages
527
528 # Type
529
530 ```
531 makeSearchPathOutput :: string -> string -> [package] -> string
532 ```
533
534 # Examples
535 :::{.example}
536 ## `lib.strings.makeSearchPathOutput` usage example
537
538 ```nix
539 makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ]
540 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin"
541 ```
542
543 :::
544 */
545 makeSearchPathOutput =
546 output:
547 subDir:
548 pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs);
549
550 /**
551 Construct a library search path (such as RPATH) containing the
552 libraries for a set of packages
553
554 # Inputs
555
556 `packages`
557 : List of packages
558
559 # Type
560
561 ```
562 makeLibraryPath :: [package] -> string
563 ```
564
565 # Examples
566 :::{.example}
567 ## `lib.strings.makeLibraryPath` usage example
568
569 ```nix
570 makeLibraryPath [ "/usr" "/usr/local" ]
571 => "/usr/lib:/usr/local/lib"
572 pkgs = import <nixpkgs> { }
573 makeLibraryPath [ pkgs.openssl pkgs.zlib ]
574 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib"
575 ```
576
577 :::
578 */
579 makeLibraryPath = makeSearchPathOutput "lib" "lib";
580
581 /**
582 Construct an include search path (such as C_INCLUDE_PATH) containing the
583 header files for a set of packages or paths.
584
585 # Inputs
586
587 `packages`
588 : List of packages
589
590 # Type
591
592 ```
593 makeIncludePath :: [package] -> string
594 ```
595
596 # Examples
597 :::{.example}
598 ## `lib.strings.makeIncludePath` usage example
599
600 ```nix
601 makeIncludePath [ "/usr" "/usr/local" ]
602 => "/usr/include:/usr/local/include"
603 pkgs = import <nixpkgs> { }
604 makeIncludePath [ pkgs.openssl pkgs.zlib ]
605 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/include:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8-dev/include"
606 ```
607
608 :::
609 */
610 makeIncludePath = makeSearchPathOutput "dev" "include";
611
612 /**
613 Construct a binary search path (such as $PATH) containing the
614 binaries for a set of packages.
615
616 # Inputs
617
618 `packages`
619 : List of packages
620
621 # Type
622
623 ```
624 makeBinPath :: [package] -> string
625 ```
626
627 # Examples
628 :::{.example}
629 ## `lib.strings.makeBinPath` usage example
630
631 ```nix
632 makeBinPath ["/root" "/usr" "/usr/local"]
633 => "/root/bin:/usr/bin:/usr/local/bin"
634 ```
635
636 :::
637 */
638 makeBinPath = makeSearchPathOutput "bin" "bin";
639
640 /**
641 Normalize path, removing extraneous /s
642
643
644 # Inputs
645
646 `s`
647 : 1\. Function argument
648
649 # Type
650
651 ```
652 normalizePath :: string -> string
653 ```
654
655 # Examples
656 :::{.example}
657 ## `lib.strings.normalizePath` usage example
658
659 ```nix
660 normalizePath "/a//b///c/"
661 => "/a/b/c/"
662 ```
663
664 :::
665 */
666 normalizePath = s:
667 warnIf
668 (isPath s)
669 ''
670 lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported.
671 Path values are always normalised in Nix, so there's no need to call this function on them.
672 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.
673 This behavior is deprecated and will throw an error in the future.''
674 (
675 builtins.foldl'
676 (x: y: if y == "/" && hasSuffix "/" x then x else x+y)
677 ""
678 (stringToCharacters s)
679 );
680
681 /**
682 Depending on the boolean `cond', return either the given string
683 or the empty string. Useful to concatenate against a bigger string.
684
685
686 # Inputs
687
688 `cond`
689 : Condition
690
691 `string`
692 : String to return if condition is true
693
694 # Type
695
696 ```
697 optionalString :: bool -> string -> string
698 ```
699
700 # Examples
701 :::{.example}
702 ## `lib.strings.optionalString` usage example
703
704 ```nix
705 optionalString true "some-string"
706 => "some-string"
707 optionalString false "some-string"
708 => ""
709 ```
710
711 :::
712 */
713 optionalString =
714 cond:
715 string: if cond then string else "";
716
717 /**
718 Determine whether a string has given prefix.
719
720
721 # Inputs
722
723 `pref`
724 : Prefix to check for
725
726 `str`
727 : Input string
728
729 # Type
730
731 ```
732 hasPrefix :: string -> string -> bool
733 ```
734
735 # Examples
736 :::{.example}
737 ## `lib.strings.hasPrefix` usage example
738
739 ```nix
740 hasPrefix "foo" "foobar"
741 => true
742 hasPrefix "foo" "barfoo"
743 => false
744 ```
745
746 :::
747 */
748 hasPrefix =
749 pref:
750 str:
751 # Before 23.05, paths would be copied to the store before converting them
752 # to strings and comparing. This was surprising and confusing.
753 warnIf
754 (isPath pref)
755 ''
756 lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported.
757 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
758 This function also copies the path to the Nix store, which may not be what you want.
759 This behavior is deprecated and will throw an error in the future.
760 You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.''
761 (substring 0 (stringLength pref) str == pref);
762
763 /**
764 Determine whether a string has given suffix.
765
766
767 # Inputs
768
769 `suffix`
770 : Suffix to check for
771
772 `content`
773 : Input string
774
775 # Type
776
777 ```
778 hasSuffix :: string -> string -> bool
779 ```
780
781 # Examples
782 :::{.example}
783 ## `lib.strings.hasSuffix` usage example
784
785 ```nix
786 hasSuffix "foo" "foobar"
787 => false
788 hasSuffix "foo" "barfoo"
789 => true
790 ```
791
792 :::
793 */
794 hasSuffix =
795 suffix:
796 content:
797 let
798 lenContent = stringLength content;
799 lenSuffix = stringLength suffix;
800 in
801 # Before 23.05, paths would be copied to the store before converting them
802 # to strings and comparing. This was surprising and confusing.
803 warnIf
804 (isPath suffix)
805 ''
806 lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
807 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
808 This function also copies the path to the Nix store, which may not be what you want.
809 This behavior is deprecated and will throw an error in the future.''
810 (
811 lenContent >= lenSuffix
812 && substring (lenContent - lenSuffix) lenContent content == suffix
813 );
814
815 /**
816 Determine whether a string contains the given infix
817
818
819 # Inputs
820
821 `infix`
822 : 1\. Function argument
823
824 `content`
825 : 2\. Function argument
826
827 # Type
828
829 ```
830 hasInfix :: string -> string -> bool
831 ```
832
833 # Examples
834 :::{.example}
835 ## `lib.strings.hasInfix` usage example
836
837 ```nix
838 hasInfix "bc" "abcd"
839 => true
840 hasInfix "ab" "abcd"
841 => true
842 hasInfix "cd" "abcd"
843 => true
844 hasInfix "foo" "abcd"
845 => false
846 ```
847
848 :::
849 */
850 hasInfix = infix: content:
851 # Before 23.05, paths would be copied to the store before converting them
852 # to strings and comparing. This was surprising and confusing.
853 warnIf
854 (isPath infix)
855 ''
856 lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported.
857 There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
858 This function also copies the path to the Nix store, which may not be what you want.
859 This behavior is deprecated and will throw an error in the future.''
860 (builtins.match ".*${escapeRegex infix}.*" "${content}" != null);
861
862 /**
863 Convert a string `s` to a list of characters (i.e. singleton strings).
864 This allows you to, e.g., map a function over each character. However,
865 note that this will likely be horribly inefficient; Nix is not a
866 general purpose programming language. Complex string manipulations
867 should, if appropriate, be done in a derivation.
868 Also note that Nix treats strings as a list of bytes and thus doesn't
869 handle unicode.
870
871
872 # Inputs
873
874 `s`
875 : 1\. Function argument
876
877 # Type
878
879 ```
880 stringToCharacters :: string -> [string]
881 ```
882
883 # Examples
884 :::{.example}
885 ## `lib.strings.stringToCharacters` usage example
886
887 ```nix
888 stringToCharacters ""
889 => [ ]
890 stringToCharacters "abc"
891 => [ "a" "b" "c" ]
892 stringToCharacters "🦄"
893 => [ "�" "�" "�" "�" ]
894 ```
895
896 :::
897 */
898 stringToCharacters = s:
899 genList (p: substring p 1 s) (stringLength s);
900
901 /**
902 Manipulate a string character by character and replace them by
903 strings before concatenating the results.
904
905
906 # Inputs
907
908 `f`
909 : Function to map over each individual character
910
911 `s`
912 : Input string
913
914 # Type
915
916 ```
917 stringAsChars :: (string -> string) -> string -> string
918 ```
919
920 # Examples
921 :::{.example}
922 ## `lib.strings.stringAsChars` usage example
923
924 ```nix
925 stringAsChars (x: if x == "a" then "i" else x) "nax"
926 => "nix"
927 ```
928
929 :::
930 */
931 stringAsChars =
932 # Function to map over each individual character
933 f:
934 # Input string
935 s: concatStrings (
936 map f (stringToCharacters s)
937 );
938
939 /**
940 Convert char to ascii value, must be in printable range
941
942
943 # Inputs
944
945 `c`
946 : 1\. Function argument
947
948 # Type
949
950 ```
951 charToInt :: string -> int
952 ```
953
954 # Examples
955 :::{.example}
956 ## `lib.strings.charToInt` usage example
957
958 ```nix
959 charToInt "A"
960 => 65
961 charToInt "("
962 => 40
963 ```
964
965 :::
966 */
967 charToInt = c: builtins.getAttr c asciiTable;
968
969 /**
970 Escape occurrence of the elements of `list` in `string` by
971 prefixing it with a backslash.
972
973
974 # Inputs
975
976 `list`
977 : 1\. Function argument
978
979 `string`
980 : 2\. Function argument
981
982 # Type
983
984 ```
985 escape :: [string] -> string -> string
986 ```
987
988 # Examples
989 :::{.example}
990 ## `lib.strings.escape` usage example
991
992 ```nix
993 escape ["(" ")"] "(foo)"
994 => "\\(foo\\)"
995 ```
996
997 :::
998 */
999 escape = list: replaceStrings list (map (c: "\\${c}") list);
1000
1001 /**
1002 Escape occurrence of the element of `list` in `string` by
1003 converting to its ASCII value and prefixing it with \\x.
1004 Only works for printable ascii characters.
1005
1006
1007 # Inputs
1008
1009 `list`
1010 : 1\. Function argument
1011
1012 `string`
1013 : 2\. Function argument
1014
1015 # Type
1016
1017 ```
1018 escapeC = [string] -> string -> string
1019 ```
1020
1021 # Examples
1022 :::{.example}
1023 ## `lib.strings.escapeC` usage example
1024
1025 ```nix
1026 escapeC [" "] "foo bar"
1027 => "foo\\x20bar"
1028 ```
1029
1030 :::
1031 */
1032 escapeC = list: replaceStrings list (map (c: "\\x${ toLower (lib.toHexString (charToInt c))}") list);
1033
1034 /**
1035 Escape the `string` so it can be safely placed inside a URL
1036 query.
1037
1038 # Inputs
1039
1040 `string`
1041 : 1\. Function argument
1042
1043 # Type
1044
1045 ```
1046 escapeURL :: string -> string
1047 ```
1048
1049 # Examples
1050 :::{.example}
1051 ## `lib.strings.escapeURL` usage example
1052
1053 ```nix
1054 escapeURL "foo/bar baz"
1055 => "foo%2Fbar%20baz"
1056 ```
1057
1058 :::
1059 */
1060 escapeURL = let
1061 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" "-" "_" "." "~" ];
1062 toEscape = builtins.removeAttrs asciiTable unreserved;
1063 in
1064 replaceStrings (builtins.attrNames toEscape) (lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") toEscape);
1065
1066 /**
1067 Quote `string` to be used safely within the Bourne shell if it has any
1068 special characters.
1069
1070
1071 # Inputs
1072
1073 `string`
1074 : 1\. Function argument
1075
1076 # Type
1077
1078 ```
1079 escapeShellArg :: string -> string
1080 ```
1081
1082 # Examples
1083 :::{.example}
1084 ## `lib.strings.escapeShellArg` usage example
1085
1086 ```nix
1087 escapeShellArg "esc'ape\nme"
1088 => "'esc'\\''ape\nme'"
1089 ```
1090
1091 :::
1092 */
1093 escapeShellArg = arg:
1094 let
1095 string = toString arg;
1096 in
1097 if match "[[:alnum:],._+:@%/-]+" string == null
1098 then "'${replaceStrings ["'"] ["'\\''"] string}'"
1099 else string;
1100
1101 /**
1102 Quote all arguments that have special characters to be safely passed to the
1103 Bourne shell.
1104
1105 # Inputs
1106
1107 `args`
1108 : 1\. Function argument
1109
1110 # Type
1111
1112 ```
1113 escapeShellArgs :: [string] -> string
1114 ```
1115
1116 # Examples
1117 :::{.example}
1118 ## `lib.strings.escapeShellArgs` usage example
1119
1120 ```nix
1121 escapeShellArgs ["one" "two three" "four'five"]
1122 => "one 'two three' 'four'\\''five'"
1123 ```
1124
1125 :::
1126 */
1127 escapeShellArgs = concatMapStringsSep " " escapeShellArg;
1128
1129 /**
1130 Test whether the given `name` is a valid POSIX shell variable name.
1131
1132
1133 # Inputs
1134
1135 `name`
1136 : 1\. Function argument
1137
1138 # Type
1139
1140 ```
1141 string -> bool
1142 ```
1143
1144 # Examples
1145 :::{.example}
1146 ## `lib.strings.isValidPosixName` usage example
1147
1148 ```nix
1149 isValidPosixName "foo_bar000"
1150 => true
1151 isValidPosixName "0-bad.jpg"
1152 => false
1153 ```
1154
1155 :::
1156 */
1157 isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
1158
1159 /**
1160 Translate a Nix value into a shell variable declaration, with proper escaping.
1161
1162 The value can be a string (mapped to a regular variable), a list of strings
1163 (mapped to a Bash-style array) or an attribute set of strings (mapped to a
1164 Bash-style associative array). Note that "string" includes string-coercible
1165 values like paths or derivations.
1166
1167 Strings are translated into POSIX sh-compatible code; lists and attribute sets
1168 assume a shell that understands Bash syntax (e.g. Bash or ZSH).
1169
1170
1171 # Inputs
1172
1173 `name`
1174 : 1\. Function argument
1175
1176 `value`
1177 : 2\. Function argument
1178
1179 # Type
1180
1181 ```
1182 string -> ( string | [string] | { ${name} :: string; } ) -> string
1183 ```
1184
1185 # Examples
1186 :::{.example}
1187 ## `lib.strings.toShellVar` usage example
1188
1189 ```nix
1190 ''
1191 ${toShellVar "foo" "some string"}
1192 [[ "$foo" == "some string" ]]
1193 ''
1194 ```
1195
1196 :::
1197 */
1198 toShellVar = name: value:
1199 lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
1200 if isAttrs value && ! isStringLike value then
1201 "declare -A ${name}=(${
1202 concatStringsSep " " (lib.mapAttrsToList (n: v:
1203 "[${escapeShellArg n}]=${escapeShellArg v}"
1204 ) value)
1205 })"
1206 else if isList value then
1207 "declare -a ${name}=(${escapeShellArgs value})"
1208 else
1209 "${name}=${escapeShellArg value}"
1210 );
1211
1212 /**
1213 Translate an attribute set `vars` into corresponding shell variable declarations
1214 using `toShellVar`.
1215
1216
1217 # Inputs
1218
1219 `vars`
1220 : 1\. Function argument
1221
1222 # Type
1223
1224 ```
1225 toShellVars :: {
1226 ${name} :: string | [ string ] | { ${key} :: string; };
1227 } -> string
1228 ```
1229
1230 # Examples
1231 :::{.example}
1232 ## `lib.strings.toShellVars` usage example
1233
1234 ```nix
1235 let
1236 foo = "value";
1237 bar = foo;
1238 in ''
1239 ${toShellVars { inherit foo bar; }}
1240 [[ "$foo" == "$bar" ]]
1241 ''
1242 ```
1243
1244 :::
1245 */
1246 toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
1247
1248 /**
1249 Turn a string `s` into a Nix expression representing that string
1250
1251 # Inputs
1252
1253 `s`
1254 : 1\. Function argument
1255
1256 # Type
1257
1258 ```
1259 escapeNixString :: string -> string
1260 ```
1261
1262 # Examples
1263 :::{.example}
1264 ## `lib.strings.escapeNixString` usage example
1265
1266 ```nix
1267 escapeNixString "hello\${}\n"
1268 => "\"hello\\\${}\\n\""
1269 ```
1270
1271 :::
1272 */
1273 escapeNixString = s: escape ["$"] (toJSON s);
1274
1275 /**
1276 Turn a string `s` into an exact regular expression
1277
1278 # Inputs
1279
1280 `s`
1281 : 1\. Function argument
1282
1283 # Type
1284
1285 ```
1286 escapeRegex :: string -> string
1287 ```
1288
1289 # Examples
1290 :::{.example}
1291 ## `lib.strings.escapeRegex` usage example
1292
1293 ```nix
1294 escapeRegex "[^a-z]*"
1295 => "\\[\\^a-z]\\*"
1296 ```
1297
1298 :::
1299 */
1300 escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
1301
1302 /**
1303 Quotes a string `s` if it can't be used as an identifier directly.
1304
1305
1306 # Inputs
1307
1308 `s`
1309 : 1\. Function argument
1310
1311 # Type
1312
1313 ```
1314 escapeNixIdentifier :: string -> string
1315 ```
1316
1317 # Examples
1318 :::{.example}
1319 ## `lib.strings.escapeNixIdentifier` usage example
1320
1321 ```nix
1322 escapeNixIdentifier "hello"
1323 => "hello"
1324 escapeNixIdentifier "0abc"
1325 => "\"0abc\""
1326 ```
1327
1328 :::
1329 */
1330 escapeNixIdentifier = s:
1331 # Regex from https://github.com/NixOS/nix/blob/d048577909e383439c2549e849c5c2f2016c997e/src/libexpr/lexer.l#L91
1332 if match "[a-zA-Z_][a-zA-Z0-9_'-]*" s != null
1333 then s else escapeNixString s;
1334
1335 /**
1336 Escapes a string `s` such that it is safe to include verbatim in an XML
1337 document.
1338
1339 # Inputs
1340
1341 `s`
1342 : 1\. Function argument
1343
1344 # Type
1345
1346 ```
1347 escapeXML :: string -> string
1348 ```
1349
1350 # Examples
1351 :::{.example}
1352 ## `lib.strings.escapeXML` usage example
1353
1354 ```nix
1355 escapeXML ''"test" 'test' < & >''
1356 => ""test" 'test' < & >"
1357 ```
1358
1359 :::
1360 */
1361 escapeXML = builtins.replaceStrings
1362 ["\"" "'" "<" ">" "&"]
1363 [""" "'" "<" ">" "&"];
1364
1365 # warning added 12-12-2022
1366 replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
1367
1368 # Case conversion utilities.
1369 lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
1370 upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1371
1372 /**
1373 Converts an ASCII string `s` to lower-case.
1374
1375 # Inputs
1376
1377 `s`
1378 : The string to convert to lower-case.
1379
1380 # Type
1381
1382 ```
1383 toLower :: string -> string
1384 ```
1385
1386 # Examples
1387 :::{.example}
1388 ## `lib.strings.toLower` usage example
1389
1390 ```nix
1391 toLower "HOME"
1392 => "home"
1393 ```
1394
1395 :::
1396 */
1397 toLower = replaceStrings upperChars lowerChars;
1398
1399 /**
1400 Converts an ASCII string `s` to upper-case.
1401
1402 # Inputs
1403
1404 `s`
1405 : The string to convert to upper-case.
1406
1407
1408 # Type
1409
1410 ```
1411 toUpper :: string -> string
1412 ```
1413
1414 # Examples
1415 :::{.example}
1416 ## `lib.strings.toUpper` usage example
1417
1418 ```nix
1419 toUpper "home"
1420 => "HOME"
1421 ```
1422
1423 :::
1424 */
1425 toUpper = replaceStrings lowerChars upperChars;
1426
1427 /**
1428 Appends string context from string like object `src` to `target`.
1429
1430 :::{.warning}
1431 This is an implementation
1432 detail of Nix and should be used carefully.
1433 :::
1434
1435 Strings in Nix carry an invisible `context` which is a list of strings
1436 representing store paths. If the string is later used in a derivation
1437 attribute, the derivation will properly populate the inputDrvs and
1438 inputSrcs.
1439
1440
1441 # Inputs
1442
1443 `src`
1444 : The string to take the context from. If the argument is not a string,
1445 it will be implicitly converted to a string.
1446
1447 `target`
1448 : The string to append the context to. If the argument is not a string,
1449 it will be implicitly converted to a string.
1450
1451 # Type
1452
1453 ```
1454 addContextFrom :: string -> string -> string
1455 ```
1456
1457 # Examples
1458 :::{.example}
1459 ## `lib.strings.addContextFrom` usage example
1460
1461 ```nix
1462 pkgs = import <nixpkgs> { };
1463 addContextFrom pkgs.coreutils "bar"
1464 => "bar"
1465 ```
1466
1467 The context can be displayed using the `toString` function:
1468
1469 ```nix
1470 nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
1471 {
1472 "/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
1473 }
1474 ```
1475
1476 :::
1477 */
1478 addContextFrom = src: target: substring 0 0 src + target;
1479
1480 /**
1481 Cut a string with a separator and produces a list of strings which
1482 were separated by this separator.
1483
1484 # Inputs
1485
1486 `sep`
1487 : 1\. Function argument
1488
1489 `s`
1490 : 2\. Function argument
1491
1492 # Type
1493
1494 ```
1495 splitString :: string -> string -> [string]
1496 ```
1497
1498 # Examples
1499 :::{.example}
1500 ## `lib.strings.splitString` usage example
1501
1502 ```nix
1503 splitString "." "foo.bar.baz"
1504 => [ "foo" "bar" "baz" ]
1505 splitString "/" "/usr/local/bin"
1506 => [ "" "usr" "local" "bin" ]
1507 ```
1508
1509 :::
1510 */
1511 splitString = sep: s:
1512 let
1513 splits = builtins.filter builtins.isString (builtins.split (escapeRegex (toString sep)) (toString s));
1514 in
1515 map (addContextFrom s) splits;
1516
1517
1518 /**
1519 Return a string without the specified prefix, if the prefix matches.
1520
1521 # Inputs
1522
1523 `prefix`
1524 : Prefix to remove if it matches
1525
1526 `str`
1527 : Input string
1528
1529 # Type
1530
1531 ```
1532 removePrefix :: string -> string -> string
1533 ```
1534
1535 # Examples
1536 :::{.example}
1537 ## `lib.strings.removePrefix` usage example
1538
1539 ```nix
1540 removePrefix "foo." "foo.bar.baz"
1541 => "bar.baz"
1542 removePrefix "xxx" "foo.bar.baz"
1543 => "foo.bar.baz"
1544 ```
1545
1546 :::
1547 */
1548 removePrefix =
1549 prefix:
1550 str:
1551 # Before 23.05, paths would be copied to the store before converting them
1552 # to strings and comparing. This was surprising and confusing.
1553 warnIf
1554 (isPath prefix)
1555 ''
1556 lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported.
1557 There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case.
1558 This function also copies the path to the Nix store, which may not be what you want.
1559 This behavior is deprecated and will throw an error in the future.''
1560 (let
1561 preLen = stringLength prefix;
1562 in
1563 if substring 0 preLen str == prefix then
1564 # -1 will take the string until the end
1565 substring preLen (-1) str
1566 else
1567 str);
1568
1569 /**
1570 Return a string without the specified suffix, if the suffix matches.
1571
1572
1573 # Inputs
1574
1575 `suffix`
1576 : Suffix to remove if it matches
1577
1578 `str`
1579 : Input string
1580
1581 # Type
1582
1583 ```
1584 removeSuffix :: string -> string -> string
1585 ```
1586
1587 # Examples
1588 :::{.example}
1589 ## `lib.strings.removeSuffix` usage example
1590
1591 ```nix
1592 removeSuffix "front" "homefront"
1593 => "home"
1594 removeSuffix "xxx" "homefront"
1595 => "homefront"
1596 ```
1597
1598 :::
1599 */
1600 removeSuffix =
1601 suffix:
1602 str:
1603 # Before 23.05, paths would be copied to the store before converting them
1604 # to strings and comparing. This was surprising and confusing.
1605 warnIf
1606 (isPath suffix)
1607 ''
1608 lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
1609 There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case.
1610 This function also copies the path to the Nix store, which may not be what you want.
1611 This behavior is deprecated and will throw an error in the future.''
1612 (let
1613 sufLen = stringLength suffix;
1614 sLen = stringLength str;
1615 in
1616 if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then
1617 substring 0 (sLen - sufLen) str
1618 else
1619 str);
1620
1621 /**
1622 Return true if string `v1` denotes a version older than `v2`.
1623
1624
1625 # Inputs
1626
1627 `v1`
1628 : 1\. Function argument
1629
1630 `v2`
1631 : 2\. Function argument
1632
1633 # Type
1634
1635 ```
1636 versionOlder :: String -> String -> Bool
1637 ```
1638
1639 # Examples
1640 :::{.example}
1641 ## `lib.strings.versionOlder` usage example
1642
1643 ```nix
1644 versionOlder "1.1" "1.2"
1645 => true
1646 versionOlder "1.1" "1.1"
1647 => false
1648 ```
1649
1650 :::
1651 */
1652 versionOlder = v1: v2: compareVersions v2 v1 == 1;
1653
1654 /**
1655 Return true if string v1 denotes a version equal to or newer than v2.
1656
1657
1658 # Inputs
1659
1660 `v1`
1661 : 1\. Function argument
1662
1663 `v2`
1664 : 2\. Function argument
1665
1666 # Type
1667
1668 ```
1669 versionAtLeast :: String -> String -> Bool
1670 ```
1671
1672 # Examples
1673 :::{.example}
1674 ## `lib.strings.versionAtLeast` usage example
1675
1676 ```nix
1677 versionAtLeast "1.1" "1.0"
1678 => true
1679 versionAtLeast "1.1" "1.1"
1680 => true
1681 versionAtLeast "1.1" "1.2"
1682 => false
1683 ```
1684
1685 :::
1686 */
1687 versionAtLeast = v1: v2: !versionOlder v1 v2;
1688
1689 /**
1690 This function takes an argument `x` that's either a derivation or a
1691 derivation's "name" attribute and extracts the name part from that
1692 argument.
1693
1694 # Inputs
1695
1696 `x`
1697 : 1\. Function argument
1698
1699 # Type
1700
1701 ```
1702 getName :: String | Derivation -> String
1703 ```
1704
1705
1706 # Examples
1707 :::{.example}
1708 ## `lib.strings.getName` usage example
1709
1710 ```nix
1711 getName "youtube-dl-2016.01.01"
1712 => "youtube-dl"
1713 getName pkgs.youtube-dl
1714 => "youtube-dl"
1715 ```
1716
1717 :::
1718 */
1719 getName = let
1720 parse = drv: (parseDrvName drv).name;
1721 in x:
1722 if isString x
1723 then parse x
1724 else x.pname or (parse x.name);
1725
1726 /**
1727 This function takes an argument `x` that's either a derivation or a
1728 derivation's "name" attribute and extracts the version part from that
1729 argument.
1730
1731
1732 # Inputs
1733
1734 `x`
1735 : 1\. Function argument
1736
1737 # Type
1738
1739 ```
1740 getVersion :: String | Derivation -> String
1741 ```
1742
1743 # Examples
1744 :::{.example}
1745 ## `lib.strings.getVersion` usage example
1746
1747 ```nix
1748 getVersion "youtube-dl-2016.01.01"
1749 => "2016.01.01"
1750 getVersion pkgs.youtube-dl
1751 => "2016.01.01"
1752 ```
1753
1754 :::
1755 */
1756 getVersion = let
1757 parse = drv: (parseDrvName drv).version;
1758 in x:
1759 if isString x
1760 then parse x
1761 else x.version or (parse x.name);
1762
1763 /**
1764 Extract name and version from a URL as shown in the examples.
1765
1766 Separator `sep` is used to determine the end of the extension.
1767
1768
1769 # Inputs
1770
1771 `url`
1772 : 1\. Function argument
1773
1774 `sep`
1775 : 2\. Function argument
1776
1777 # Type
1778
1779 ```
1780 nameFromURL :: String -> String
1781 ```
1782
1783 # Examples
1784 :::{.example}
1785 ## `lib.strings.nameFromURL` usage example
1786
1787 ```nix
1788 nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
1789 => "nix"
1790 nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
1791 => "nix-1.7-x86"
1792 ```
1793
1794 :::
1795 */
1796 nameFromURL = url: sep:
1797 let
1798 components = splitString "/" url;
1799 filename = lib.last components;
1800 name = head (splitString sep filename);
1801 in assert name != filename; name;
1802
1803 /**
1804 Create a `"-D<feature>:<type>=<value>"` string that can be passed to typical
1805 CMake invocations.
1806
1807 # Inputs
1808
1809 `feature`
1810 : The feature to be set
1811
1812 `type`
1813 : The type of the feature to be set, as described in
1814 https://cmake.org/cmake/help/latest/command/set.html
1815 the possible values (case insensitive) are:
1816 BOOL FILEPATH PATH STRING INTERNAL
1817
1818 `value`
1819 : The desired value
1820
1821 # Type
1822
1823 ```
1824 cmakeOptionType :: string -> string -> string -> string
1825 ```
1826
1827 # Examples
1828 :::{.example}
1829 ## `lib.strings.cmakeOptionType` usage example
1830
1831 ```nix
1832 cmakeOptionType "string" "ENGINE" "sdl2"
1833 => "-DENGINE:STRING=sdl2"
1834 ```
1835
1836 :::
1837 */
1838 cmakeOptionType = let
1839 types = [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ];
1840 in type: feature: value:
1841 assert (elem (toUpper type) types);
1842 assert (isString feature);
1843 assert (isString value);
1844 "-D${feature}:${toUpper type}=${value}";
1845
1846 /**
1847 Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
1848 CMake invocations.
1849
1850
1851 # Inputs
1852
1853 `condition`
1854 : The condition to be made true or false
1855
1856 `flag`
1857 : The controlling flag of the condition
1858
1859 # Type
1860
1861 ```
1862 cmakeBool :: string -> bool -> string
1863 ```
1864
1865 # Examples
1866 :::{.example}
1867 ## `lib.strings.cmakeBool` usage example
1868
1869 ```nix
1870 cmakeBool "ENABLE_STATIC_LIBS" false
1871 => "-DENABLESTATIC_LIBS:BOOL=FALSE"
1872 ```
1873
1874 :::
1875 */
1876 cmakeBool = condition: flag:
1877 assert (lib.isString condition);
1878 assert (lib.isBool flag);
1879 cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag));
1880
1881 /**
1882 Create a -D<feature>:STRING=<value> string that can be passed to typical
1883 CMake invocations.
1884 This is the most typical usage, so it deserves a special case.
1885
1886
1887 # Inputs
1888
1889 `feature`
1890 : The feature to be set
1891
1892 `value`
1893 : The desired value
1894
1895
1896 # Type
1897
1898 ```
1899 cmakeFeature :: string -> string -> string
1900 ```
1901
1902 # Examples
1903 :::{.example}
1904 ## `lib.strings.cmakeFeature` usage example
1905
1906 ```nix
1907 cmakeFeature "MODULES" "badblock"
1908 => "-DMODULES:STRING=badblock"
1909 ```
1910
1911 :::
1912 */
1913 cmakeFeature = feature: value:
1914 assert (lib.isString feature);
1915 assert (lib.isString value);
1916 cmakeOptionType "string" feature value;
1917
1918 /**
1919 Create a -D<feature>=<value> string that can be passed to typical Meson
1920 invocations.
1921
1922
1923 # Inputs
1924
1925 `feature`
1926 : The feature to be set
1927
1928 `value`
1929 : The desired value
1930
1931 # Type
1932
1933 ```
1934 mesonOption :: string -> string -> string
1935 ```
1936
1937 # Examples
1938 :::{.example}
1939 ## `lib.strings.mesonOption` usage example
1940
1941 ```nix
1942 mesonOption "engine" "opengl"
1943 => "-Dengine=opengl"
1944 ```
1945
1946 :::
1947 */
1948 mesonOption = feature: value:
1949 assert (lib.isString feature);
1950 assert (lib.isString value);
1951 "-D${feature}=${value}";
1952
1953 /**
1954 Create a -D<condition>={true,false} string that can be passed to typical
1955 Meson invocations.
1956
1957
1958 # Inputs
1959
1960 `condition`
1961 : The condition to be made true or false
1962
1963 `flag`
1964 : The controlling flag of the condition
1965
1966 # Type
1967
1968 ```
1969 mesonBool :: string -> bool -> string
1970 ```
1971
1972 # Examples
1973 :::{.example}
1974 ## `lib.strings.mesonBool` usage example
1975
1976 ```nix
1977 mesonBool "hardened" true
1978 => "-Dhardened=true"
1979 mesonBool "static" false
1980 => "-Dstatic=false"
1981 ```
1982
1983 :::
1984 */
1985 mesonBool = condition: flag:
1986 assert (lib.isString condition);
1987 assert (lib.isBool flag);
1988 mesonOption condition (lib.boolToString flag);
1989
1990 /**
1991 Create a -D<feature>={enabled,disabled} string that can be passed to
1992 typical Meson invocations.
1993
1994
1995 # Inputs
1996
1997 `feature`
1998 : The feature to be enabled or disabled
1999
2000 `flag`
2001 : The controlling flag
2002
2003 # Type
2004
2005 ```
2006 mesonEnable :: string -> bool -> string
2007 ```
2008
2009 # Examples
2010 :::{.example}
2011 ## `lib.strings.mesonEnable` usage example
2012
2013 ```nix
2014 mesonEnable "docs" true
2015 => "-Ddocs=enabled"
2016 mesonEnable "savage" false
2017 => "-Dsavage=disabled"
2018 ```
2019
2020 :::
2021 */
2022 mesonEnable = feature: flag:
2023 assert (lib.isString feature);
2024 assert (lib.isBool flag);
2025 mesonOption feature (if flag then "enabled" else "disabled");
2026
2027 /**
2028 Create an --{enable,disable}-<feature> string that can be passed to
2029 standard GNU Autoconf scripts.
2030
2031
2032 # Inputs
2033
2034 `flag`
2035 : 1\. Function argument
2036
2037 `feature`
2038 : 2\. Function argument
2039
2040 # Type
2041
2042 ```
2043 enableFeature :: bool -> string -> string
2044 ```
2045
2046 # Examples
2047 :::{.example}
2048 ## `lib.strings.enableFeature` usage example
2049
2050 ```nix
2051 enableFeature true "shared"
2052 => "--enable-shared"
2053 enableFeature false "shared"
2054 => "--disable-shared"
2055 ```
2056
2057 :::
2058 */
2059 enableFeature = flag: feature:
2060 assert lib.isBool flag;
2061 assert lib.isString feature; # e.g. passing openssl instead of "openssl"
2062 "--${if flag then "enable" else "disable"}-${feature}";
2063
2064 /**
2065 Create an --{enable-<feature>=<value>,disable-<feature>} string that can be passed to
2066 standard GNU Autoconf scripts.
2067
2068
2069 # Inputs
2070
2071 `flag`
2072 : 1\. Function argument
2073
2074 `feature`
2075 : 2\. Function argument
2076
2077 `value`
2078 : 3\. Function argument
2079
2080 # Type
2081
2082 ```
2083 enableFeatureAs :: bool -> string -> string -> string
2084 ```
2085
2086 # Examples
2087 :::{.example}
2088 ## `lib.strings.enableFeatureAs` usage example
2089
2090 ```nix
2091 enableFeatureAs true "shared" "foo"
2092 => "--enable-shared=foo"
2093 enableFeatureAs false "shared" (throw "ignored")
2094 => "--disable-shared"
2095 ```
2096
2097 :::
2098 */
2099 enableFeatureAs = flag: feature: value:
2100 enableFeature flag feature + optionalString flag "=${value}";
2101
2102 /**
2103 Create an --{with,without}-<feature> string that can be passed to
2104 standard GNU Autoconf scripts.
2105
2106
2107 # Inputs
2108
2109 `flag`
2110 : 1\. Function argument
2111
2112 `feature`
2113 : 2\. Function argument
2114
2115
2116 # Type
2117
2118 ```
2119 withFeature :: bool -> string -> string
2120 ```
2121
2122 # Examples
2123 :::{.example}
2124 ## `lib.strings.withFeature` usage example
2125
2126 ```nix
2127 withFeature true "shared"
2128 => "--with-shared"
2129 withFeature false "shared"
2130 => "--without-shared"
2131 ```
2132
2133 :::
2134 */
2135 withFeature = flag: feature:
2136 assert isString feature; # e.g. passing openssl instead of "openssl"
2137 "--${if flag then "with" else "without"}-${feature}";
2138
2139 /**
2140 Create an --{with-<feature>=<value>,without-<feature>} string that can be passed to
2141 standard GNU Autoconf scripts.
2142
2143
2144 # Inputs
2145
2146 `flag`
2147 : 1\. Function argument
2148
2149 `feature`
2150 : 2\. Function argument
2151
2152 `value`
2153 : 3\. Function argument
2154
2155 # Type
2156
2157 ```
2158 withFeatureAs :: bool -> string -> string -> string
2159 ```
2160
2161
2162 # Examples
2163 :::{.example}
2164 ## `lib.strings.withFeatureAs` usage example
2165
2166 ```nix
2167 withFeatureAs true "shared" "foo"
2168 => "--with-shared=foo"
2169 withFeatureAs false "shared" (throw "ignored")
2170 => "--without-shared"
2171 ```
2172
2173 :::
2174 */
2175 withFeatureAs = flag: feature: value:
2176 withFeature flag feature + optionalString flag "=${value}";
2177
2178 /**
2179 Create a fixed width string with additional prefix to match
2180 required width.
2181
2182 This function will fail if the input string is longer than the
2183 requested length.
2184
2185
2186 # Inputs
2187
2188 `width`
2189 : 1\. Function argument
2190
2191 `filler`
2192 : 2\. Function argument
2193
2194 `str`
2195 : 3\. Function argument
2196
2197 # Type
2198
2199 ```
2200 fixedWidthString :: int -> string -> string -> string
2201 ```
2202
2203 # Examples
2204 :::{.example}
2205 ## `lib.strings.fixedWidthString` usage example
2206
2207 ```nix
2208 fixedWidthString 5 "0" (toString 15)
2209 => "00015"
2210 ```
2211
2212 :::
2213 */
2214 fixedWidthString = width: filler: str:
2215 let
2216 strw = lib.stringLength str;
2217 reqWidth = width - (lib.stringLength filler);
2218 in
2219 assert lib.assertMsg (strw <= width)
2220 "fixedWidthString: requested string length (${
2221 toString width}) must not be shorter than actual length (${
2222 toString strw})";
2223 if strw == width then str else filler + fixedWidthString reqWidth filler str;
2224
2225 /**
2226 Format a number adding leading zeroes up to fixed width.
2227
2228
2229 # Inputs
2230
2231 `width`
2232 : 1\. Function argument
2233
2234 `n`
2235 : 2\. Function argument
2236
2237 # Type
2238
2239 ```
2240 fixedWidthNumber :: int -> int -> string
2241 ```
2242
2243 # Examples
2244 :::{.example}
2245 ## `lib.strings.fixedWidthNumber` usage example
2246
2247 ```nix
2248 fixedWidthNumber 5 15
2249 => "00015"
2250 ```
2251
2252 :::
2253 */
2254 fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
2255
2256 /**
2257 Convert a float to a string, but emit a warning when precision is lost
2258 during the conversion
2259
2260
2261 # Inputs
2262
2263 `float`
2264 : 1\. Function argument
2265
2266
2267 # Type
2268
2269 ```
2270 floatToString :: float -> string
2271 ```
2272
2273 # Examples
2274 :::{.example}
2275 ## `lib.strings.floatToString` usage example
2276
2277 ```nix
2278 floatToString 0.000001
2279 => "0.000001"
2280 floatToString 0.0000001
2281 => trace: warning: Imprecise conversion from float to string 0.000000
2282 "0.000000"
2283 ```
2284
2285 :::
2286 */
2287 floatToString = float: let
2288 result = toString float;
2289 precise = float == fromJSON result;
2290 in lib.warnIf (!precise) "Imprecise conversion from float to string ${result}"
2291 result;
2292
2293 /**
2294 Check whether a value `val` can be coerced to a string.
2295
2296 :::{.warning}
2297 Soft-deprecated function. While the original implementation is available as
2298 `isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
2299 :::
2300
2301 # Inputs
2302
2303 `val`
2304 : 1\. Function argument
2305
2306 # Type
2307
2308 ```
2309 isCoercibleToString :: a -> bool
2310 ```
2311 */
2312 isCoercibleToString = lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2305)
2313 "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."
2314 isConvertibleWithToString;
2315
2316 /**
2317 Check whether a list or other value `x` can be passed to toString.
2318
2319 Many types of value are coercible to string this way, including `int`, `float`,
2320 `null`, `bool`, `list` of similarly coercible values.
2321
2322 # Inputs
2323
2324 `val`
2325 : 1\. Function argument
2326
2327 # Type
2328
2329 ```
2330 isConvertibleWithToString :: a -> bool
2331 ```
2332 */
2333 isConvertibleWithToString = let
2334 types = [ "null" "int" "float" "bool" ];
2335 in x:
2336 isStringLike x ||
2337 elem (typeOf x) types ||
2338 (isList x && lib.all isConvertibleWithToString x);
2339
2340 /**
2341 Check whether a value can be coerced to a string.
2342 The value must be a string, path, or attribute set.
2343
2344 String-like values can be used without explicit conversion in
2345 string interpolations and in most functions that expect a string.
2346
2347
2348 # Inputs
2349
2350 `x`
2351 : 1\. Function argument
2352
2353 # Type
2354
2355 ```
2356 isStringLike :: a -> bool
2357 ```
2358 */
2359 isStringLike = x:
2360 isString x ||
2361 isPath x ||
2362 x ? outPath ||
2363 x ? __toString;
2364
2365 /**
2366 Check whether a value `x` is a store path.
2367
2368
2369 # Inputs
2370
2371 `x`
2372 : 1\. Function argument
2373
2374 # Type
2375
2376 ```
2377 isStorePath :: a -> bool
2378 ```
2379
2380 # Examples
2381 :::{.example}
2382 ## `lib.strings.isStorePath` usage example
2383
2384 ```nix
2385 isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
2386 => false
2387 isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"
2388 => true
2389 isStorePath pkgs.python
2390 => true
2391 isStorePath [] || isStorePath 42 || isStorePath {} || …
2392 => false
2393 ```
2394
2395 :::
2396 */
2397 isStorePath = x:
2398 if isStringLike x then
2399 let str = toString x; in
2400 substring 0 1 str == "/"
2401 && dirOf str == storeDir
2402 else
2403 false;
2404
2405 /**
2406 Parse a string as an int. Does not support parsing of integers with preceding zero due to
2407 ambiguity between zero-padded and octal numbers. See toIntBase10.
2408
2409 # Inputs
2410
2411 `str`
2412 : A string to be interpreted as an int.
2413
2414 # Type
2415
2416 ```
2417 toInt :: string -> int
2418 ```
2419
2420 # Examples
2421 :::{.example}
2422 ## `lib.strings.toInt` usage example
2423
2424 ```nix
2425 toInt "1337"
2426 => 1337
2427
2428 toInt "-4"
2429 => -4
2430
2431 toInt " 123 "
2432 => 123
2433
2434 toInt "00024"
2435 => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
2436
2437 toInt "3.14"
2438 => error: floating point JSON numbers are not supported
2439 ```
2440
2441 :::
2442 */
2443 toInt =
2444 let
2445 matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*";
2446 matchLeadingZero = match "0[[:digit:]]+";
2447 in
2448 str:
2449 let
2450 # RegEx: Match any leading whitespace, possibly a '-', one or more digits,
2451 # and finally match any trailing whitespace.
2452 strippedInput = matchStripInput str;
2453
2454 # RegEx: Match a leading '0' then one or more digits.
2455 isLeadingZero = matchLeadingZero (head strippedInput) == [];
2456
2457 # Attempt to parse input
2458 parsedInput = fromJSON (head strippedInput);
2459
2460 generalError = "toInt: Could not convert ${escapeNixString str} to int.";
2461
2462 in
2463 # Error on presence of non digit characters.
2464 if strippedInput == null
2465 then throw generalError
2466 # Error on presence of leading zero/octal ambiguity.
2467 else if isLeadingZero
2468 then throw "toInt: Ambiguity in interpretation of ${escapeNixString str} between octal and zero padded integer."
2469 # Error if parse function fails.
2470 else if !isInt parsedInput
2471 then throw generalError
2472 # Return result.
2473 else parsedInput;
2474
2475
2476 /**
2477 Parse a string as a base 10 int. This supports parsing of zero-padded integers.
2478
2479 # Inputs
2480
2481 `str`
2482 : A string to be interpreted as an int.
2483
2484 # Type
2485
2486 ```
2487 toIntBase10 :: string -> int
2488 ```
2489
2490 # Examples
2491 :::{.example}
2492 ## `lib.strings.toIntBase10` usage example
2493
2494 ```nix
2495 toIntBase10 "1337"
2496 => 1337
2497
2498 toIntBase10 "-4"
2499 => -4
2500
2501 toIntBase10 " 123 "
2502 => 123
2503
2504 toIntBase10 "00024"
2505 => 24
2506
2507 toIntBase10 "3.14"
2508 => error: floating point JSON numbers are not supported
2509 ```
2510
2511 :::
2512 */
2513 toIntBase10 =
2514 let
2515 matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*";
2516 matchZero = match "0+";
2517 in
2518 str:
2519 let
2520 # RegEx: Match any leading whitespace, then match any zero padding,
2521 # capture possibly a '-' followed by one or more digits,
2522 # and finally match any trailing whitespace.
2523 strippedInput = matchStripInput str;
2524
2525 # RegEx: Match at least one '0'.
2526 isZero = matchZero (head strippedInput) == [];
2527
2528 # Attempt to parse input
2529 parsedInput = fromJSON (head strippedInput);
2530
2531 generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";
2532
2533 in
2534 # Error on presence of non digit characters.
2535 if strippedInput == null
2536 then throw generalError
2537 # In the special case zero-padded zero (00000), return early.
2538 else if isZero
2539 then 0
2540 # Error if parse function fails.
2541 else if !isInt parsedInput
2542 then throw generalError
2543 # Return result.
2544 else parsedInput;
2545
2546 /**
2547 Read a list of paths from `file`, relative to the `rootPath`.
2548 Lines beginning with `#` are treated as comments and ignored.
2549 Whitespace is significant.
2550
2551 :::{.warning}
2552 This function is deprecated and should be avoided.
2553 :::
2554
2555 :::{.note}
2556 This function is not performant and should be avoided.
2557 :::
2558
2559 # Inputs
2560
2561 `rootPath`
2562 : 1\. Function argument
2563
2564 `file`
2565 : 2\. Function argument
2566
2567 # Type
2568
2569 ```
2570 readPathsFromFile :: string -> string -> [string]
2571 ```
2572
2573 # Examples
2574 :::{.example}
2575 ## `lib.strings.readPathsFromFile` usage example
2576
2577 ```nix
2578 readPathsFromFile /prefix
2579 ./pkgs/development/libraries/qt-5/5.4/qtbase/series
2580 => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
2581 "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
2582 "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
2583 "/prefix/nix-profiles-library-paths.patch"
2584 "/prefix/compose-search-path.patch" ]
2585 ```
2586
2587 :::
2588 */
2589 readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead."
2590 (rootPath: file:
2591 let
2592 lines = lib.splitString "\n" (readFile file);
2593 removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line));
2594 relativePaths = removeComments lines;
2595 absolutePaths = map (path: rootPath + "/${path}") relativePaths;
2596 in
2597 absolutePaths);
2598
2599 /**
2600 Read the contents of a file removing the trailing \n
2601
2602
2603 # Inputs
2604
2605 `file`
2606 : 1\. Function argument
2607
2608 # Type
2609
2610 ```
2611 fileContents :: path -> string
2612 ```
2613
2614 # Examples
2615 :::{.example}
2616 ## `lib.strings.fileContents` usage example
2617
2618 ```nix
2619 $ echo "1.0" > ./version
2620
2621 fileContents ./version
2622 => "1.0"
2623 ```
2624
2625 :::
2626 */
2627 fileContents = file: removeSuffix "\n" (readFile file);
2628
2629
2630 /**
2631 Creates a valid derivation name from a potentially invalid one.
2632
2633 # Inputs
2634
2635 `string`
2636 : 1\. Function argument
2637
2638 # Type
2639
2640 ```
2641 sanitizeDerivationName :: String -> String
2642 ```
2643
2644 # Examples
2645 :::{.example}
2646 ## `lib.strings.sanitizeDerivationName` usage example
2647
2648 ```nix
2649 sanitizeDerivationName "../hello.bar # foo"
2650 => "-hello.bar-foo"
2651 sanitizeDerivationName ""
2652 => "unknown"
2653 sanitizeDerivationName pkgs.hello
2654 => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10"
2655 ```
2656
2657 :::
2658 */
2659 sanitizeDerivationName =
2660 let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*";
2661 in
2662 string:
2663 # First detect the common case of already valid strings, to speed those up
2664 if stringLength string <= 207 && okRegex string != null
2665 then unsafeDiscardStringContext string
2666 else lib.pipe string [
2667 # Get rid of string context. This is safe under the assumption that the
2668 # resulting string is only used as a derivation name
2669 unsafeDiscardStringContext
2670 # Strip all leading "."
2671 (x: elemAt (match "\\.*(.*)" x) 0)
2672 # Split out all invalid characters
2673 # https://github.com/NixOS/nix/blob/2.3.2/src/libstore/store-api.cc#L85-L112
2674 # https://github.com/NixOS/nix/blob/2242be83c61788b9c0736a92bb0b5c7bbfc40803/nix-rust/src/store/path.rs#L100-L125
2675 (split "[^[:alnum:]+._?=-]+")
2676 # Replace invalid character ranges with a "-"
2677 (concatMapStrings (s: if lib.isList s then "-" else s))
2678 # Limit to 211 characters (minus 4 chars for ".drv")
2679 (x: substring (lib.max (stringLength x - 207) 0) (-1) x)
2680 # If the result is empty, replace it with "unknown"
2681 (x: if stringLength x == 0 then "unknown" else x)
2682 ];
2683
2684 /**
2685 Computes the Levenshtein distance between two strings `a` and `b`.
2686
2687 Complexity O(n*m) where n and m are the lengths of the strings.
2688 Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742
2689
2690
2691 # Inputs
2692
2693 `a`
2694 : 1\. Function argument
2695
2696 `b`
2697 : 2\. Function argument
2698
2699 # Type
2700
2701 ```
2702 levenshtein :: string -> string -> int
2703 ```
2704
2705 # Examples
2706 :::{.example}
2707 ## `lib.strings.levenshtein` usage example
2708
2709 ```nix
2710 levenshtein "foo" "foo"
2711 => 0
2712 levenshtein "book" "hook"
2713 => 1
2714 levenshtein "hello" "Heyo"
2715 => 3
2716 ```
2717
2718 :::
2719 */
2720 levenshtein = a: b: let
2721 # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1)
2722 arr = lib.genList (i:
2723 lib.genList (j:
2724 dist i j
2725 ) (stringLength b + 1)
2726 ) (stringLength a + 1);
2727 d = x: y: lib.elemAt (lib.elemAt arr x) y;
2728 dist = i: j:
2729 let c = if substring (i - 1) 1 a == substring (j - 1) 1 b
2730 then 0 else 1;
2731 in
2732 if j == 0 then i
2733 else if i == 0 then j
2734 else lib.min
2735 ( lib.min (d (i - 1) j + 1) (d i (j - 1) + 1))
2736 ( d (i - 1) (j - 1) + c );
2737 in d (stringLength a) (stringLength b);
2738
2739 /**
2740 Returns the length of the prefix that appears in both strings `a` and `b`.
2741
2742
2743 # Inputs
2744
2745 `a`
2746 : 1\. Function argument
2747
2748 `b`
2749 : 2\. Function argument
2750
2751 # Type
2752
2753 ```
2754 commonPrefixLength :: string -> string -> int
2755 ```
2756 */
2757 commonPrefixLength = a: b:
2758 let
2759 m = lib.min (stringLength a) (stringLength b);
2760 go = i: if i >= m then m else if substring i 1 a == substring i 1 b then go (i + 1) else i;
2761 in go 0;
2762
2763 /**
2764 Returns the length of the suffix common to both strings `a` and `b`.
2765
2766
2767 # Inputs
2768
2769 `a`
2770 : 1\. Function argument
2771
2772 `b`
2773 : 2\. Function argument
2774
2775 # Type
2776
2777 ```
2778 commonSuffixLength :: string -> string -> int
2779 ```
2780 */
2781 commonSuffixLength = a: b:
2782 let
2783 m = lib.min (stringLength a) (stringLength b);
2784 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;
2785 in go 0;
2786
2787 /**
2788 Returns whether the levenshtein distance between two strings `a` and `b` is at most some value `k`.
2789
2790 Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise
2791
2792 # Inputs
2793
2794 `k`
2795 : Distance threshold
2796
2797 `a`
2798 : String `a`
2799
2800 `b`
2801 : String `b`
2802
2803 # Type
2804
2805 ```
2806 levenshteinAtMost :: int -> string -> string -> bool
2807 ```
2808
2809 # Examples
2810 :::{.example}
2811 ## `lib.strings.levenshteinAtMost` usage example
2812
2813 ```nix
2814 levenshteinAtMost 0 "foo" "foo"
2815 => true
2816 levenshteinAtMost 1 "foo" "boa"
2817 => false
2818 levenshteinAtMost 2 "foo" "boa"
2819 => true
2820 levenshteinAtMost 2 "This is a sentence" "this is a sentense."
2821 => false
2822 levenshteinAtMost 3 "This is a sentence" "this is a sentense."
2823 => true
2824 ```
2825
2826 :::
2827 */
2828 levenshteinAtMost = let
2829 infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1;
2830
2831 # This function takes two strings stripped by their common pre and suffix,
2832 # and returns whether they differ by at most two by Levenshtein distance.
2833 # Because of this stripping, if they do indeed differ by at most two edits,
2834 # we know that those edits were (if at all) done at the start or the end,
2835 # while the middle has to have stayed the same. This fact is used in the
2836 # implementation.
2837 infixDifferAtMost2 = x: y:
2838 let
2839 xlen = stringLength x;
2840 ylen = stringLength y;
2841 # This function is only called with |x| >= |y| and |x| - |y| <= 2, so
2842 # diff is one of 0, 1 or 2
2843 diff = xlen - ylen;
2844
2845 # Infix of x and y, stripped by the left and right most character
2846 xinfix = substring 1 (xlen - 2) x;
2847 yinfix = substring 1 (ylen - 2) y;
2848
2849 # x and y but a character deleted at the left or right
2850 xdelr = substring 0 (xlen - 1) x;
2851 xdell = substring 1 (xlen - 1) x;
2852 ydelr = substring 0 (ylen - 1) y;
2853 ydell = substring 1 (ylen - 1) y;
2854 in
2855 # A length difference of 2 can only be gotten with 2 delete edits,
2856 # which have to have happened at the start and end of x
2857 # Example: "abcdef" -> "bcde"
2858 if diff == 2 then xinfix == y
2859 # A length difference of 1 can only be gotten with a deletion on the
2860 # right and a replacement on the left or vice versa.
2861 # Example: "abcdef" -> "bcdez" or "zbcde"
2862 else if diff == 1 then xinfix == ydelr || xinfix == ydell
2863 # No length difference can either happen through replacements on both
2864 # sides, or a deletion on the left and an insertion on the right or
2865 # vice versa
2866 # Example: "abcdef" -> "zbcdez" or "bcdefz" or "zabcde"
2867 else xinfix == yinfix || xdelr == ydell || xdell == ydelr;
2868
2869 in k: if k <= 0 then a: b: a == b else
2870 let f = a: b:
2871 let
2872 alen = stringLength a;
2873 blen = stringLength b;
2874 prelen = commonPrefixLength a b;
2875 suflen = commonSuffixLength a b;
2876 presuflen = prelen + suflen;
2877 ainfix = substring prelen (alen - presuflen) a;
2878 binfix = substring prelen (blen - presuflen) b;
2879 in
2880 # Make a be the bigger string
2881 if alen < blen then f b a
2882 # If a has over k more characters than b, even with k deletes on a, b can't be reached
2883 else if alen - blen > k then false
2884 else if k == 1 then infixDifferAtMost1 ainfix binfix
2885 else if k == 2 then infixDifferAtMost2 ainfix binfix
2886 else levenshtein ainfix binfix <= k;
2887 in f;
2888}