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