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 Concatenate a list of strings, adding a newline at the end of each one.
274 Defined as `concatMapStrings (s: s + "\n")`.
275
276 # Inputs
277
278 `list`
279 : List of strings. Any element that is not a string will be implicitly converted to a string.
280
281 # Type
282
283 ```
284 concatLines :: [string] -> string
285 ```
286
287 # Examples
288 :::{.example}
289 ## `lib.strings.concatLines` usage example
290
291 ```nix
292 concatLines [ "foo" "bar" ]
293 => "foo\nbar\n"
294 ```
295
296 :::
297 */
298 concatLines = concatMapStrings (s: s + "\n");
299
300 /**
301 Repeat a string `n` times,
302 and concatenate the parts into a new string.
303
304
305 # Inputs
306
307 `n`
308 : 1\. Function argument
309
310 `s`
311 : 2\. Function argument
312
313 # Type
314
315 ```
316 replicate :: int -> string -> string
317 ```
318
319 # Examples
320 :::{.example}
321 ## `lib.strings.replicate` usage example
322
323 ```nix
324 replicate 3 "v"
325 => "vvv"
326 replicate 5 "hello"
327 => "hellohellohellohellohello"
328 ```
329
330 :::
331 */
332 replicate = n: s: concatStrings (lib.lists.replicate n s);
333
334 /**
335 Remove leading and trailing whitespace from a string `s`.
336
337 Whitespace is defined as any of the following characters:
338 " ", "\t" "\r" "\n"
339
340 # Inputs
341
342 `s`
343 : The string to trim
344
345 # Type
346
347 ```
348 trim :: string -> string
349 ```
350
351 # Examples
352 :::{.example}
353 ## `lib.strings.trim` usage example
354
355 ```nix
356 trim " hello, world! "
357 => "hello, world!"
358 ```
359
360 :::
361 */
362 trim = trimWith {
363 start = true;
364 end = true;
365 };
366
367 /**
368 Remove leading and/or trailing whitespace from a string `s`.
369
370 To remove both leading and trailing whitespace, you can also use [`trim`](#function-library-lib.strings.trim)
371
372 Whitespace is defined as any of the following characters:
373 " ", "\t" "\r" "\n"
374
375 # Inputs
376
377 `config` (Attribute set)
378 : `start`
379 : Whether to trim leading whitespace (`false` by default)
380
381 : `end`
382 : Whether to trim trailing whitespace (`false` by default)
383
384 `s`
385 : The string to trim
386
387 # Type
388
389 ```
390 trimWith :: { start :: Bool; end :: Bool } -> String -> String
391 ```
392
393 # Examples
394 :::{.example}
395 ## `lib.strings.trimWith` usage example
396
397 ```nix
398 trimWith { start = true; } " hello, world! "}
399 => "hello, world! "
400
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";
416
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 "(.*)";
428
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);
434
435 /**
436 Construct a Unix-style, colon-separated search path consisting of
437 the given `subDir` appended to each of the given paths.
438
439 # Inputs
440
441 `subDir`
442 : Directory name to append
443
444 `paths`
445 : List of base paths
446
447 # Type
448
449 ```
450 makeSearchPath :: string -> [string] -> string
451 ```
452
453 # Examples
454 :::{.example}
455 ## `lib.strings.makeSearchPath` usage example
456
457 ```nix
458 makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
459 => "/root/bin:/usr/bin:/usr/local/bin"
460 makeSearchPath "bin" [""]
461 => "/bin"
462 ```
463
464 :::
465 */
466 makeSearchPath =
467 subDir:
468 paths:
469 concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths));
470
471 /**
472 Construct a Unix-style search path by appending the given
473 `subDir` to the specified `output` of each of the packages.
474
475 If no output by the given name is found, fallback to `.out` and then to
476 the default.
477
478
479 # Inputs
480
481 `output`
482 : Package output to use
483
484 `subDir`
485 : Directory name to append
486
487 `pkgs`
488 : List of packages
489
490 # Type
491
492 ```
493 makeSearchPathOutput :: string -> string -> [package] -> string
494 ```
495
496 # Examples
497 :::{.example}
498 ## `lib.strings.makeSearchPathOutput` usage example
499
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 ```
504
505 :::
506 */
507 makeSearchPathOutput =
508 output:
509 subDir:
510 pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs);
511
512 /**
513 Construct a library search path (such as RPATH) containing the
514 libraries for a set of packages
515
516 # Inputs
517
518 `packages`
519 : List of packages
520
521 # Type
522
523 ```
524 makeLibraryPath :: [package] -> string
525 ```
526
527 # Examples
528 :::{.example}
529 ## `lib.strings.makeLibraryPath` usage example
530
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 ```
538
539 :::
540 */
541 makeLibraryPath = makeSearchPathOutput "lib" "lib";
542
543 /**
544 Construct an include search path (such as C_INCLUDE_PATH) containing the
545 header files for a set of packages or paths.
546
547 # Inputs
548
549 `packages`
550 : List of packages
551
552 # Type
553
554 ```
555 makeIncludePath :: [package] -> string
556 ```
557
558 # Examples
559 :::{.example}
560 ## `lib.strings.makeIncludePath` usage example
561
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 ```
569
570 :::
571 */
572 makeIncludePath = makeSearchPathOutput "dev" "include";
573
574 /**
575 Construct a binary search path (such as $PATH) containing the
576 binaries for a set of packages.
577
578 # Inputs
579
580 `packages`
581 : List of packages
582
583 # Type
584
585 ```
586 makeBinPath :: [package] -> string
587 ```
588
589 # Examples
590 :::{.example}
591 ## `lib.strings.makeBinPath` usage example
592
593 ```nix
594 makeBinPath ["/root" "/usr" "/usr/local"]
595 => "/root/bin:/usr/bin:/usr/local/bin"
596 ```
597
598 :::
599 */
600 makeBinPath = makeSearchPathOutput "bin" "bin";
601
602 /**
603 Normalize path, removing extraneous /s
604
605
606 # Inputs
607
608 `s`
609 : 1\. Function argument
610
611 # Type
612
613 ```
614 normalizePath :: string -> string
615 ```
616
617 # Examples
618 :::{.example}
619 ## `lib.strings.normalizePath` usage example
620
621 ```nix
622 normalizePath "/a//b///c/"
623 => "/a/b/c/"
624 ```
625
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 );
642
643 /**
644 Depending on the boolean `cond', return either the given string
645 or the empty string. Useful to concatenate against a bigger string.
646
647
648 # Inputs
649
650 `cond`
651 : Condition
652
653 `string`
654 : String to return if condition is true
655
656 # Type
657
658 ```
659 optionalString :: bool -> string -> string
660 ```
661
662 # Examples
663 :::{.example}
664 ## `lib.strings.optionalString` usage example
665
666 ```nix
667 optionalString true "some-string"
668 => "some-string"
669 optionalString false "some-string"
670 => ""
671 ```
672
673 :::
674 */
675 optionalString =
676 cond:
677 string: if cond then string else "";
678
679 /**
680 Determine whether a string has given prefix.
681
682
683 # Inputs
684
685 `pref`
686 : Prefix to check for
687
688 `str`
689 : Input string
690
691 # Type
692
693 ```
694 hasPrefix :: string -> string -> bool
695 ```
696
697 # Examples
698 :::{.example}
699 ## `lib.strings.hasPrefix` usage example
700
701 ```nix
702 hasPrefix "foo" "foobar"
703 => true
704 hasPrefix "foo" "barfoo"
705 => false
706 ```
707
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);
724
725 /**
726 Determine whether a string has given suffix.
727
728
729 # Inputs
730
731 `suffix`
732 : Suffix to check for
733
734 `content`
735 : Input string
736
737 # Type
738
739 ```
740 hasSuffix :: string -> string -> bool
741 ```
742
743 # Examples
744 :::{.example}
745 ## `lib.strings.hasSuffix` usage example
746
747 ```nix
748 hasSuffix "foo" "foobar"
749 => false
750 hasSuffix "foo" "barfoo"
751 => true
752 ```
753
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 );
776
777 /**
778 Determine whether a string contains the given infix
779
780
781 # Inputs
782
783 `infix`
784 : 1\. Function argument
785
786 `content`
787 : 2\. Function argument
788
789 # Type
790
791 ```
792 hasInfix :: string -> string -> bool
793 ```
794
795 # Examples
796 :::{.example}
797 ## `lib.strings.hasInfix` usage example
798
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 ```
809
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);
823
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.
832
833
834 # Inputs
835
836 `s`
837 : 1\. Function argument
838
839 # Type
840
841 ```
842 stringToCharacters :: string -> [string]
843 ```
844
845 # Examples
846 :::{.example}
847 ## `lib.strings.stringToCharacters` usage example
848
849 ```nix
850 stringToCharacters ""
851 => [ ]
852 stringToCharacters "abc"
853 => [ "a" "b" "c" ]
854 stringToCharacters "🦄"
855 => [ "�" "�" "�" "�" ]
856 ```
857
858 :::
859 */
860 stringToCharacters = s:
861 genList (p: substring p 1 s) (stringLength s);
862
863 /**
864 Manipulate a string character by character and replace them by
865 strings before concatenating the results.
866
867
868 # Inputs
869
870 `f`
871 : Function to map over each individual character
872
873 `s`
874 : Input string
875
876 # Type
877
878 ```
879 stringAsChars :: (string -> string) -> string -> string
880 ```
881
882 # Examples
883 :::{.example}
884 ## `lib.strings.stringAsChars` usage example
885
886 ```nix
887 stringAsChars (x: if x == "a" then "i" else x) "nax"
888 => "nix"
889 ```
890
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 );
900
901 /**
902 Convert char to ascii value, must be in printable range
903
904
905 # Inputs
906
907 `c`
908 : 1\. Function argument
909
910 # Type
911
912 ```
913 charToInt :: string -> int
914 ```
915
916 # Examples
917 :::{.example}
918 ## `lib.strings.charToInt` usage example
919
920 ```nix
921 charToInt "A"
922 => 65
923 charToInt "("
924 => 40
925 ```
926
927 :::
928 */
929 charToInt = c: builtins.getAttr c asciiTable;
930
931 /**
932 Escape occurrence of the elements of `list` in `string` by
933 prefixing it with a backslash.
934
935
936 # Inputs
937
938 `list`
939 : 1\. Function argument
940
941 `string`
942 : 2\. Function argument
943
944 # Type
945
946 ```
947 escape :: [string] -> string -> string
948 ```
949
950 # Examples
951 :::{.example}
952 ## `lib.strings.escape` usage example
953
954 ```nix
955 escape ["(" ")"] "(foo)"
956 => "\\(foo\\)"
957 ```
958
959 :::
960 */
961 escape = list: replaceStrings list (map (c: "\\${c}") list);
962
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.
967
968
969 # Inputs
970
971 `list`
972 : 1\. Function argument
973
974 `string`
975 : 2\. Function argument
976
977 # Type
978
979 ```
980 escapeC = [string] -> string -> string
981 ```
982
983 # Examples
984 :::{.example}
985 ## `lib.strings.escapeC` usage example
986
987 ```nix
988 escapeC [" "] "foo bar"
989 => "foo\\x20bar"
990 ```
991
992 :::
993 */
994 escapeC = list: replaceStrings list (map (c: "\\x${ toLower (lib.toHexString (charToInt c))}") list);
995
996 /**
997 Escape the `string` so it can be safely placed inside a URL
998 query.
999
1000 # Inputs
1001
1002 `string`
1003 : 1\. Function argument
1004
1005 # Type
1006
1007 ```
1008 escapeURL :: string -> string
1009 ```
1010
1011 # Examples
1012 :::{.example}
1013 ## `lib.strings.escapeURL` usage example
1014
1015 ```nix
1016 escapeURL "foo/bar baz"
1017 => "foo%2Fbar%20baz"
1018 ```
1019
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);
1027
1028 /**
1029 Quote `string` to be used safely within the Bourne shell.
1030
1031
1032 # Inputs
1033
1034 `string`
1035 : 1\. Function argument
1036
1037 # Type
1038
1039 ```
1040 escapeShellArg :: string -> string
1041 ```
1042
1043 # Examples
1044 :::{.example}
1045 ## `lib.strings.escapeShellArg` usage example
1046
1047 ```nix
1048 escapeShellArg "esc'ape\nme"
1049 => "'esc'\\''ape\nme'"
1050 ```
1051
1052 :::
1053 */
1054 escapeShellArg = arg: "'${replaceStrings ["'"] ["'\\''"] (toString arg)}'";
1055
1056 /**
1057 Quote all arguments to be safely passed to the Bourne shell.
1058
1059 # Inputs
1060
1061 `args`
1062 : 1\. Function argument
1063
1064 # Type
1065
1066 ```
1067 escapeShellArgs :: [string] -> string
1068 ```
1069
1070 # Examples
1071 :::{.example}
1072 ## `lib.strings.escapeShellArgs` usage example
1073
1074 ```nix
1075 escapeShellArgs ["one" "two three" "four'five"]
1076 => "'one' 'two three' 'four'\\''five'"
1077 ```
1078
1079 :::
1080 */
1081 escapeShellArgs = concatMapStringsSep " " escapeShellArg;
1082
1083 /**
1084 Test whether the given `name` is a valid POSIX shell variable name.
1085
1086
1087 # Inputs
1088
1089 `name`
1090 : 1\. Function argument
1091
1092 # Type
1093
1094 ```
1095 string -> bool
1096 ```
1097
1098 # Examples
1099 :::{.example}
1100 ## `lib.strings.isValidPosixName` usage example
1101
1102 ```nix
1103 isValidPosixName "foo_bar000"
1104 => true
1105 isValidPosixName "0-bad.jpg"
1106 => false
1107 ```
1108
1109 :::
1110 */
1111 isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
1112
1113 /**
1114 Translate a Nix value into a shell variable declaration, with proper escaping.
1115
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.
1120
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).
1123
1124
1125 # Inputs
1126
1127 `name`
1128 : 1\. Function argument
1129
1130 `value`
1131 : 2\. Function argument
1132
1133 # Type
1134
1135 ```
1136 string -> ( string | [string] | { ${name} :: string; } ) -> string
1137 ```
1138
1139 # Examples
1140 :::{.example}
1141 ## `lib.strings.toShellVar` usage example
1142
1143 ```nix
1144 ''
1145 ${toShellVar "foo" "some string"}
1146 [[ "$foo" == "some string" ]]
1147 ''
1148 ```
1149
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 );
1165
1166 /**
1167 Translate an attribute set `vars` into corresponding shell variable declarations
1168 using `toShellVar`.
1169
1170
1171 # Inputs
1172
1173 `vars`
1174 : 1\. Function argument
1175
1176 # Type
1177
1178 ```
1179 toShellVars :: {
1180 ${name} :: string | [ string ] | { ${key} :: string; };
1181 } -> string
1182 ```
1183
1184 # Examples
1185 :::{.example}
1186 ## `lib.strings.toShellVars` usage example
1187
1188 ```nix
1189 let
1190 foo = "value";
1191 bar = foo;
1192 in ''
1193 ${toShellVars { inherit foo bar; }}
1194 [[ "$foo" == "$bar" ]]
1195 ''
1196 ```
1197
1198 :::
1199 */
1200 toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
1201
1202 /**
1203 Turn a string `s` into a Nix expression representing that string
1204
1205 # Inputs
1206
1207 `s`
1208 : 1\. Function argument
1209
1210 # Type
1211
1212 ```
1213 escapeNixString :: string -> string
1214 ```
1215
1216 # Examples
1217 :::{.example}
1218 ## `lib.strings.escapeNixString` usage example
1219
1220 ```nix
1221 escapeNixString "hello\${}\n"
1222 => "\"hello\\\${}\\n\""
1223 ```
1224
1225 :::
1226 */
1227 escapeNixString = s: escape ["$"] (toJSON s);
1228
1229 /**
1230 Turn a string `s` into an exact regular expression
1231
1232 # Inputs
1233
1234 `s`
1235 : 1\. Function argument
1236
1237 # Type
1238
1239 ```
1240 escapeRegex :: string -> string
1241 ```
1242
1243 # Examples
1244 :::{.example}
1245 ## `lib.strings.escapeRegex` usage example
1246
1247 ```nix
1248 escapeRegex "[^a-z]*"
1249 => "\\[\\^a-z]\\*"
1250 ```
1251
1252 :::
1253 */
1254 escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
1255
1256 /**
1257 Quotes a string `s` if it can't be used as an identifier directly.
1258
1259
1260 # Inputs
1261
1262 `s`
1263 : 1\. Function argument
1264
1265 # Type
1266
1267 ```
1268 escapeNixIdentifier :: string -> string
1269 ```
1270
1271 # Examples
1272 :::{.example}
1273 ## `lib.strings.escapeNixIdentifier` usage example
1274
1275 ```nix
1276 escapeNixIdentifier "hello"
1277 => "hello"
1278 escapeNixIdentifier "0abc"
1279 => "\"0abc\""
1280 ```
1281
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;
1288
1289 /**
1290 Escapes a string `s` such that it is safe to include verbatim in an XML
1291 document.
1292
1293 # Inputs
1294
1295 `s`
1296 : 1\. Function argument
1297
1298 # Type
1299
1300 ```
1301 escapeXML :: string -> string
1302 ```
1303
1304 # Examples
1305 :::{.example}
1306 ## `lib.strings.escapeXML` usage example
1307
1308 ```nix
1309 escapeXML ''"test" 'test' < & >''
1310 => ""test" 'test' < & >"
1311 ```
1312
1313 :::
1314 */
1315 escapeXML = builtins.replaceStrings
1316 ["\"" "'" "<" ">" "&"]
1317 [""" "'" "<" ">" "&"];
1318
1319 # warning added 12-12-2022
1320 replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
1321
1322 # Case conversion utilities.
1323 lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
1324 upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1325
1326 /**
1327 Converts an ASCII string `s` to lower-case.
1328
1329 # Inputs
1330
1331 `s`
1332 : The string to convert to lower-case.
1333
1334 # Type
1335
1336 ```
1337 toLower :: string -> string
1338 ```
1339
1340 # Examples
1341 :::{.example}
1342 ## `lib.strings.toLower` usage example
1343
1344 ```nix
1345 toLower "HOME"
1346 => "home"
1347 ```
1348
1349 :::
1350 */
1351 toLower = replaceStrings upperChars lowerChars;
1352
1353 /**
1354 Converts an ASCII string `s` to upper-case.
1355
1356 # Inputs
1357
1358 `s`
1359 : The string to convert to upper-case.
1360
1361
1362 # Type
1363
1364 ```
1365 toUpper :: string -> string
1366 ```
1367
1368 # Examples
1369 :::{.example}
1370 ## `lib.strings.toUpper` usage example
1371
1372 ```nix
1373 toUpper "home"
1374 => "HOME"
1375 ```
1376
1377 :::
1378 */
1379 toUpper = replaceStrings lowerChars upperChars;
1380
1381 /**
1382 Appends string context from string like object `src` to `target`.
1383
1384 :::{.warning}
1385 This is an implementation
1386 detail of Nix and should be used carefully.
1387 :::
1388
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.
1393
1394
1395 # Inputs
1396
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.
1400
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.
1404
1405 # Type
1406
1407 ```
1408 addContextFrom :: string -> string -> string
1409 ```
1410
1411 # Examples
1412 :::{.example}
1413 ## `lib.strings.addContextFrom` usage example
1414
1415 ```nix
1416 pkgs = import <nixpkgs> { };
1417 addContextFrom pkgs.coreutils "bar"
1418 => "bar"
1419 ```
1420
1421 The context can be displayed using the `toString` function:
1422
1423 ```nix
1424 nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
1425 {
1426 "/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
1427 }
1428 ```
1429
1430 :::
1431 */
1432 addContextFrom = src: target: substring 0 0 src + target;
1433
1434 /**
1435 Cut a string with a separator and produces a list of strings which
1436 were separated by this separator.
1437
1438 # Inputs
1439
1440 `sep`
1441 : 1\. Function argument
1442
1443 `s`
1444 : 2\. Function argument
1445
1446 # Type
1447
1448 ```
1449 splitString :: string -> string -> [string]
1450 ```
1451
1452 # Examples
1453 :::{.example}
1454 ## `lib.strings.splitString` usage example
1455
1456 ```nix
1457 splitString "." "foo.bar.baz"
1458 => [ "foo" "bar" "baz" ]
1459 splitString "/" "/usr/local/bin"
1460 => [ "" "usr" "local" "bin" ]
1461 ```
1462
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;
1470
1471
1472 /**
1473 Return a string without the specified prefix, if the prefix matches.
1474
1475 # Inputs
1476
1477 `prefix`
1478 : Prefix to remove if it matches
1479
1480 `str`
1481 : Input string
1482
1483 # Type
1484
1485 ```
1486 removePrefix :: string -> string -> string
1487 ```
1488
1489 # Examples
1490 :::{.example}
1491 ## `lib.strings.removePrefix` usage example
1492
1493 ```nix
1494 removePrefix "foo." "foo.bar.baz"
1495 => "bar.baz"
1496 removePrefix "xxx" "foo.bar.baz"
1497 => "foo.bar.baz"
1498 ```
1499
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);
1522
1523 /**
1524 Return a string without the specified suffix, if the suffix matches.
1525
1526
1527 # Inputs
1528
1529 `suffix`
1530 : Suffix to remove if it matches
1531
1532 `str`
1533 : Input string
1534
1535 # Type
1536
1537 ```
1538 removeSuffix :: string -> string -> string
1539 ```
1540
1541 # Examples
1542 :::{.example}
1543 ## `lib.strings.removeSuffix` usage example
1544
1545 ```nix
1546 removeSuffix "front" "homefront"
1547 => "home"
1548 removeSuffix "xxx" "homefront"
1549 => "homefront"
1550 ```
1551
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);
1574
1575 /**
1576 Return true if string `v1` denotes a version older than `v2`.
1577
1578
1579 # Inputs
1580
1581 `v1`
1582 : 1\. Function argument
1583
1584 `v2`
1585 : 2\. Function argument
1586
1587 # Type
1588
1589 ```
1590 versionOlder :: String -> String -> Bool
1591 ```
1592
1593 # Examples
1594 :::{.example}
1595 ## `lib.strings.versionOlder` usage example
1596
1597 ```nix
1598 versionOlder "1.1" "1.2"
1599 => true
1600 versionOlder "1.1" "1.1"
1601 => false
1602 ```
1603
1604 :::
1605 */
1606 versionOlder = v1: v2: compareVersions v2 v1 == 1;
1607
1608 /**
1609 Return true if string v1 denotes a version equal to or newer than v2.
1610
1611
1612 # Inputs
1613
1614 `v1`
1615 : 1\. Function argument
1616
1617 `v2`
1618 : 2\. Function argument
1619
1620 # Type
1621
1622 ```
1623 versionAtLeast :: String -> String -> Bool
1624 ```
1625
1626 # Examples
1627 :::{.example}
1628 ## `lib.strings.versionAtLeast` usage example
1629
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 ```
1638
1639 :::
1640 */
1641 versionAtLeast = v1: v2: !versionOlder v1 v2;
1642
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.
1647
1648 # Inputs
1649
1650 `x`
1651 : 1\. Function argument
1652
1653 # Type
1654
1655 ```
1656 getName :: String | Derivation -> String
1657 ```
1658
1659
1660 # Examples
1661 :::{.example}
1662 ## `lib.strings.getName` usage example
1663
1664 ```nix
1665 getName "youtube-dl-2016.01.01"
1666 => "youtube-dl"
1667 getName pkgs.youtube-dl
1668 => "youtube-dl"
1669 ```
1670
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);
1679
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.
1684
1685
1686 # Inputs
1687
1688 `x`
1689 : 1\. Function argument
1690
1691 # Type
1692
1693 ```
1694 getVersion :: String | Derivation -> String
1695 ```
1696
1697 # Examples
1698 :::{.example}
1699 ## `lib.strings.getVersion` usage example
1700
1701 ```nix
1702 getVersion "youtube-dl-2016.01.01"
1703 => "2016.01.01"
1704 getVersion pkgs.youtube-dl
1705 => "2016.01.01"
1706 ```
1707
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);
1716
1717 /**
1718 Extract name and version from a URL as shown in the examples.
1719
1720 Separator `sep` is used to determine the end of the extension.
1721
1722
1723 # Inputs
1724
1725 `url`
1726 : 1\. Function argument
1727
1728 `sep`
1729 : 2\. Function argument
1730
1731 # Type
1732
1733 ```
1734 nameFromURL :: String -> String
1735 ```
1736
1737 # Examples
1738 :::{.example}
1739 ## `lib.strings.nameFromURL` usage example
1740
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 ```
1747
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;
1756
1757 /**
1758 Create a `"-D<feature>:<type>=<value>"` string that can be passed to typical
1759 CMake invocations.
1760
1761 # Inputs
1762
1763 `feature`
1764 : The feature to be set
1765
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
1771
1772 `value`
1773 : The desired value
1774
1775 # Type
1776
1777 ```
1778 cmakeOptionType :: string -> string -> string -> string
1779 ```
1780
1781 # Examples
1782 :::{.example}
1783 ## `lib.strings.cmakeOptionType` usage example
1784
1785 ```nix
1786 cmakeOptionType "string" "ENGINE" "sdl2"
1787 => "-DENGINE:STRING=sdl2"
1788 ```
1789
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}";
1799
1800 /**
1801 Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
1802 CMake invocations.
1803
1804
1805 # Inputs
1806
1807 `condition`
1808 : The condition to be made true or false
1809
1810 `flag`
1811 : The controlling flag of the condition
1812
1813 # Type
1814
1815 ```
1816 cmakeBool :: string -> bool -> string
1817 ```
1818
1819 # Examples
1820 :::{.example}
1821 ## `lib.strings.cmakeBool` usage example
1822
1823 ```nix
1824 cmakeBool "ENABLE_STATIC_LIBS" false
1825 => "-DENABLESTATIC_LIBS:BOOL=FALSE"
1826 ```
1827
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));
1834
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.
1839
1840
1841 # Inputs
1842
1843 `feature`
1844 : The feature to be set
1845
1846 `value`
1847 : The desired value
1848
1849
1850 # Type
1851
1852 ```
1853 cmakeFeature :: string -> string -> string
1854 ```
1855
1856 # Examples
1857 :::{.example}
1858 ## `lib.strings.cmakeFeature` usage example
1859
1860 ```nix
1861 cmakeFeature "MODULES" "badblock"
1862 => "-DMODULES:STRING=badblock"
1863 ```
1864
1865 :::
1866 */
1867 cmakeFeature = feature: value:
1868 assert (lib.isString feature);
1869 assert (lib.isString value);
1870 cmakeOptionType "string" feature value;
1871
1872 /**
1873 Create a -D<feature>=<value> string that can be passed to typical Meson
1874 invocations.
1875
1876
1877 # Inputs
1878
1879 `feature`
1880 : The feature to be set
1881
1882 `value`
1883 : The desired value
1884
1885 # Type
1886
1887 ```
1888 mesonOption :: string -> string -> string
1889 ```
1890
1891 # Examples
1892 :::{.example}
1893 ## `lib.strings.mesonOption` usage example
1894
1895 ```nix
1896 mesonOption "engine" "opengl"
1897 => "-Dengine=opengl"
1898 ```
1899
1900 :::
1901 */
1902 mesonOption = feature: value:
1903 assert (lib.isString feature);
1904 assert (lib.isString value);
1905 "-D${feature}=${value}";
1906
1907 /**
1908 Create a -D<condition>={true,false} string that can be passed to typical
1909 Meson invocations.
1910
1911
1912 # Inputs
1913
1914 `condition`
1915 : The condition to be made true or false
1916
1917 `flag`
1918 : The controlling flag of the condition
1919
1920 # Type
1921
1922 ```
1923 mesonBool :: string -> bool -> string
1924 ```
1925
1926 # Examples
1927 :::{.example}
1928 ## `lib.strings.mesonBool` usage example
1929
1930 ```nix
1931 mesonBool "hardened" true
1932 => "-Dhardened=true"
1933 mesonBool "static" false
1934 => "-Dstatic=false"
1935 ```
1936
1937 :::
1938 */
1939 mesonBool = condition: flag:
1940 assert (lib.isString condition);
1941 assert (lib.isBool flag);
1942 mesonOption condition (lib.boolToString flag);
1943
1944 /**
1945 Create a -D<feature>={enabled,disabled} string that can be passed to
1946 typical Meson invocations.
1947
1948
1949 # Inputs
1950
1951 `feature`
1952 : The feature to be enabled or disabled
1953
1954 `flag`
1955 : The controlling flag
1956
1957 # Type
1958
1959 ```
1960 mesonEnable :: string -> bool -> string
1961 ```
1962
1963 # Examples
1964 :::{.example}
1965 ## `lib.strings.mesonEnable` usage example
1966
1967 ```nix
1968 mesonEnable "docs" true
1969 => "-Ddocs=enabled"
1970 mesonEnable "savage" false
1971 => "-Dsavage=disabled"
1972 ```
1973
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");
1980
1981 /**
1982 Create an --{enable,disable}-<feature> string that can be passed to
1983 standard GNU Autoconf scripts.
1984
1985
1986 # Inputs
1987
1988 `flag`
1989 : 1\. Function argument
1990
1991 `feature`
1992 : 2\. Function argument
1993
1994 # Type
1995
1996 ```
1997 enableFeature :: bool -> string -> string
1998 ```
1999
2000 # Examples
2001 :::{.example}
2002 ## `lib.strings.enableFeature` usage example
2003
2004 ```nix
2005 enableFeature true "shared"
2006 => "--enable-shared"
2007 enableFeature false "shared"
2008 => "--disable-shared"
2009 ```
2010
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}";
2017
2018 /**
2019 Create an --{enable-<feature>=<value>,disable-<feature>} string that can be passed to
2020 standard GNU Autoconf scripts.
2021
2022
2023 # Inputs
2024
2025 `flag`
2026 : 1\. Function argument
2027
2028 `feature`
2029 : 2\. Function argument
2030
2031 `value`
2032 : 3\. Function argument
2033
2034 # Type
2035
2036 ```
2037 enableFeatureAs :: bool -> string -> string -> string
2038 ```
2039
2040 # Examples
2041 :::{.example}
2042 ## `lib.strings.enableFeatureAs` usage example
2043
2044 ```nix
2045 enableFeatureAs true "shared" "foo"
2046 => "--enable-shared=foo"
2047 enableFeatureAs false "shared" (throw "ignored")
2048 => "--disable-shared"
2049 ```
2050
2051 :::
2052 */
2053 enableFeatureAs = flag: feature: value:
2054 enableFeature flag feature + optionalString flag "=${value}";
2055
2056 /**
2057 Create an --{with,without}-<feature> string that can be passed to
2058 standard GNU Autoconf scripts.
2059
2060
2061 # Inputs
2062
2063 `flag`
2064 : 1\. Function argument
2065
2066 `feature`
2067 : 2\. Function argument
2068
2069
2070 # Type
2071
2072 ```
2073 withFeature :: bool -> string -> string
2074 ```
2075
2076 # Examples
2077 :::{.example}
2078 ## `lib.strings.withFeature` usage example
2079
2080 ```nix
2081 withFeature true "shared"
2082 => "--with-shared"
2083 withFeature false "shared"
2084 => "--without-shared"
2085 ```
2086
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}";
2092
2093 /**
2094 Create an --{with-<feature>=<value>,without-<feature>} string that can be passed to
2095 standard GNU Autoconf scripts.
2096
2097
2098 # Inputs
2099
2100 `flag`
2101 : 1\. Function argument
2102
2103 `feature`
2104 : 2\. Function argument
2105
2106 `value`
2107 : 3\. Function argument
2108
2109 # Type
2110
2111 ```
2112 withFeatureAs :: bool -> string -> string -> string
2113 ```
2114
2115
2116 # Examples
2117 :::{.example}
2118 ## `lib.strings.withFeatureAs` usage example
2119
2120 ```nix
2121 withFeatureAs true "shared" "foo"
2122 => "--with-shared=foo"
2123 withFeatureAs false "shared" (throw "ignored")
2124 => "--without-shared"
2125 ```
2126
2127 :::
2128 */
2129 withFeatureAs = flag: feature: value:
2130 withFeature flag feature + optionalString flag "=${value}";
2131
2132 /**
2133 Create a fixed width string with additional prefix to match
2134 required width.
2135
2136 This function will fail if the input string is longer than the
2137 requested length.
2138
2139
2140 # Inputs
2141
2142 `width`
2143 : 1\. Function argument
2144
2145 `filler`
2146 : 2\. Function argument
2147
2148 `str`
2149 : 3\. Function argument
2150
2151 # Type
2152
2153 ```
2154 fixedWidthString :: int -> string -> string -> string
2155 ```
2156
2157 # Examples
2158 :::{.example}
2159 ## `lib.strings.fixedWidthString` usage example
2160
2161 ```nix
2162 fixedWidthString 5 "0" (toString 15)
2163 => "00015"
2164 ```
2165
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;
2178
2179 /**
2180 Format a number adding leading zeroes up to fixed width.
2181
2182
2183 # Inputs
2184
2185 `width`
2186 : 1\. Function argument
2187
2188 `n`
2189 : 2\. Function argument
2190
2191 # Type
2192
2193 ```
2194 fixedWidthNumber :: int -> int -> string
2195 ```
2196
2197 # Examples
2198 :::{.example}
2199 ## `lib.strings.fixedWidthNumber` usage example
2200
2201 ```nix
2202 fixedWidthNumber 5 15
2203 => "00015"
2204 ```
2205
2206 :::
2207 */
2208 fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
2209
2210 /**
2211 Convert a float to a string, but emit a warning when precision is lost
2212 during the conversion
2213
2214
2215 # Inputs
2216
2217 `float`
2218 : 1\. Function argument
2219
2220
2221 # Type
2222
2223 ```
2224 floatToString :: float -> string
2225 ```
2226
2227 # Examples
2228 :::{.example}
2229 ## `lib.strings.floatToString` usage example
2230
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 ```
2238
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;
2246
2247 /**
2248 Check whether a value `val` can be coerced to a string.
2249
2250 :::{.warning}
2251 Soft-deprecated function. While the original implementation is available as
2252 `isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
2253 :::
2254
2255 # Inputs
2256
2257 `val`
2258 : 1\. Function argument
2259
2260 # Type
2261
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;
2269
2270 /**
2271 Check whether a list or other value `x` can be passed to toString.
2272
2273 Many types of value are coercible to string this way, including `int`, `float`,
2274 `null`, `bool`, `list` of similarly coercible values.
2275
2276 # Inputs
2277
2278 `val`
2279 : 1\. Function argument
2280
2281 # Type
2282
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);
2293
2294 /**
2295 Check whether a value can be coerced to a string.
2296 The value must be a string, path, or attribute set.
2297
2298 String-like values can be used without explicit conversion in
2299 string interpolations and in most functions that expect a string.
2300
2301
2302 # Inputs
2303
2304 `x`
2305 : 1\. Function argument
2306
2307 # Type
2308
2309 ```
2310 isStringLike :: a -> bool
2311 ```
2312 */
2313 isStringLike = x:
2314 isString x ||
2315 isPath x ||
2316 x ? outPath ||
2317 x ? __toString;
2318
2319 /**
2320 Check whether a value `x` is a store path.
2321
2322
2323 # Inputs
2324
2325 `x`
2326 : 1\. Function argument
2327
2328 # Type
2329
2330 ```
2331 isStorePath :: a -> bool
2332 ```
2333
2334 # Examples
2335 :::{.example}
2336 ## `lib.strings.isStorePath` usage example
2337
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 ```
2348
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;
2358
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.
2362
2363 # Inputs
2364
2365 `str`
2366 : A string to be interpreted as an int.
2367
2368 # Type
2369
2370 ```
2371 toInt :: string -> int
2372 ```
2373
2374 # Examples
2375 :::{.example}
2376 ## `lib.strings.toInt` usage example
2377
2378 ```nix
2379 toInt "1337"
2380 => 1337
2381
2382 toInt "-4"
2383 => -4
2384
2385 toInt " 123 "
2386 => 123
2387
2388 toInt "00024"
2389 => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
2390
2391 toInt "3.14"
2392 => error: floating point JSON numbers are not supported
2393 ```
2394
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;
2407
2408 # RegEx: Match a leading '0' then one or more digits.
2409 isLeadingZero = matchLeadingZero (head strippedInput) == [];
2410
2411 # Attempt to parse input
2412 parsedInput = fromJSON (head strippedInput);
2413
2414 generalError = "toInt: Could not convert ${escapeNixString str} to int.";
2415
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;
2428
2429
2430 /**
2431 Parse a string as a base 10 int. This supports parsing of zero-padded integers.
2432
2433 # Inputs
2434
2435 `str`
2436 : A string to be interpreted as an int.
2437
2438 # Type
2439
2440 ```
2441 toIntBase10 :: string -> int
2442 ```
2443
2444 # Examples
2445 :::{.example}
2446 ## `lib.strings.toIntBase10` usage example
2447
2448 ```nix
2449 toIntBase10 "1337"
2450 => 1337
2451
2452 toIntBase10 "-4"
2453 => -4
2454
2455 toIntBase10 " 123 "
2456 => 123
2457
2458 toIntBase10 "00024"
2459 => 24
2460
2461 toIntBase10 "3.14"
2462 => error: floating point JSON numbers are not supported
2463 ```
2464
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;
2478
2479 # RegEx: Match at least one '0'.
2480 isZero = matchZero (head strippedInput) == [];
2481
2482 # Attempt to parse input
2483 parsedInput = fromJSON (head strippedInput);
2484
2485 generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";
2486
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;
2499
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.
2504
2505 :::{.warning}
2506 This function is deprecated and should be avoided.
2507 :::
2508
2509 :::{.note}
2510 This function is not performant and should be avoided.
2511 :::
2512
2513 # Inputs
2514
2515 `rootPath`
2516 : 1\. Function argument
2517
2518 `file`
2519 : 2\. Function argument
2520
2521 # Type
2522
2523 ```
2524 readPathsFromFile :: string -> string -> [string]
2525 ```
2526
2527 # Examples
2528 :::{.example}
2529 ## `lib.strings.readPathsFromFile` usage example
2530
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 ```
2540
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);
2552
2553 /**
2554 Read the contents of a file removing the trailing \n
2555
2556
2557 # Inputs
2558
2559 `file`
2560 : 1\. Function argument
2561
2562 # Type
2563
2564 ```
2565 fileContents :: path -> string
2566 ```
2567
2568 # Examples
2569 :::{.example}
2570 ## `lib.strings.fileContents` usage example
2571
2572 ```nix
2573 $ echo "1.0" > ./version
2574
2575 fileContents ./version
2576 => "1.0"
2577 ```
2578
2579 :::
2580 */
2581 fileContents = file: removeSuffix "\n" (readFile file);
2582
2583
2584 /**
2585 Creates a valid derivation name from a potentially invalid one.
2586
2587 # Inputs
2588
2589 `string`
2590 : 1\. Function argument
2591
2592 # Type
2593
2594 ```
2595 sanitizeDerivationName :: String -> String
2596 ```
2597
2598 # Examples
2599 :::{.example}
2600 ## `lib.strings.sanitizeDerivationName` usage example
2601
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 ```
2610
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 ];
2637
2638 /**
2639 Computes the Levenshtein distance between two strings `a` and `b`.
2640
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
2643
2644
2645 # Inputs
2646
2647 `a`
2648 : 1\. Function argument
2649
2650 `b`
2651 : 2\. Function argument
2652
2653 # Type
2654
2655 ```
2656 levenshtein :: string -> string -> int
2657 ```
2658
2659 # Examples
2660 :::{.example}
2661 ## `lib.strings.levenshtein` usage example
2662
2663 ```nix
2664 levenshtein "foo" "foo"
2665 => 0
2666 levenshtein "book" "hook"
2667 => 1
2668 levenshtein "hello" "Heyo"
2669 => 3
2670 ```
2671
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);
2692
2693 /**
2694 Returns the length of the prefix that appears in both strings `a` and `b`.
2695
2696
2697 # Inputs
2698
2699 `a`
2700 : 1\. Function argument
2701
2702 `b`
2703 : 2\. Function argument
2704
2705 # Type
2706
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;
2716
2717 /**
2718 Returns the length of the suffix common to both strings `a` and `b`.
2719
2720
2721 # Inputs
2722
2723 `a`
2724 : 1\. Function argument
2725
2726 `b`
2727 : 2\. Function argument
2728
2729 # Type
2730
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;
2740
2741 /**
2742 Returns whether the levenshtein distance between two strings `a` and `b` is at most some value `k`.
2743
2744 Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise
2745
2746 # Inputs
2747
2748 `k`
2749 : Distance threshold
2750
2751 `a`
2752 : String `a`
2753
2754 `b`
2755 : String `b`
2756
2757 # Type
2758
2759 ```
2760 levenshteinAtMost :: int -> string -> string -> bool
2761 ```
2762
2763 # Examples
2764 :::{.example}
2765 ## `lib.strings.levenshteinAtMost` usage example
2766
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 ```
2779
2780 :::
2781 */
2782 levenshteinAtMost = let
2783 infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1;
2784
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;
2798
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;
2802
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;
2822
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;
2842}