Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at fix-function-merge 2842 lines 59 kB view raw
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 => "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;" 1311 ``` 1312 1313 ::: 1314 */ 1315 escapeXML = builtins.replaceStrings 1316 ["\"" "'" "<" ">" "&"] 1317 ["&quot;" "&apos;" "&lt;" "&gt;" "&amp;"]; 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}