diff options
author | Matt Sturgeon | 2024-05-28 18:22:43 +0100 |
---|---|---|
committer | Matt Sturgeon | 2024-07-25 23:43:33 +0100 |
commit | aad87c2aa8d00fe8a9c4d8198cf166d652f8f28c (patch) | |
tree | 393fab90dfcd7380d2d231fdbeed75d55e6547f5 /lib | |
parent | d1e70dde57184785861ce9cf4e0ec10e378ffa65 (diff) |
lib.strings: add `trim` and `trimWith`
`strings.trim` returns a copy of the string with all leading and trailing whitespace removed. `strings.trimWith` does the same thing, but calling code can decide whether to trim the start and/or end of the string.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/default.nix | 2 | ||||
-rw-r--r-- | lib/strings.nix | 62 | ||||
-rw-r--r-- | lib/tests/misc.nix | 66 |
3 files changed, 129 insertions, 1 deletions
diff --git a/lib/default.nix b/lib/default.nix index d637ca203f0e..63a31101eee7 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -105,7 +105,7 @@ let hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape escapeShellArg escapeShellArgs isStorePath isStringLike - isValidPosixName toShellVar toShellVars + isValidPosixName toShellVar toShellVars trim trimWith escapeRegex escapeURL escapeXML replaceChars lowerChars upperChars toLower toUpper addContextFrom splitString removePrefix removeSuffix versionOlder versionAtLeast diff --git a/lib/strings.nix b/lib/strings.nix index 67bb669d04e0..18ef707750bb 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -157,6 +157,68 @@ rec { */ replicate = n: s: concatStrings (lib.lists.replicate n s); + /* + Remove leading and trailing whitespace from a string. + + Whitespace is defined as any of the following characters: + " ", "\t" "\r" "\n" + + Type: trim :: string -> string + + Example: + trim " hello, world! " + => "hello, world!" + */ + trim = trimWith { + start = true; + end = true; + }; + + /* + Remove leading and/or trailing whitespace from a string. + + Whitespace is defined as any of the following characters: + " ", "\t" "\r" "\n" + + Type: trimWith :: Attrs -> string -> string + + Example: + trimWith { start = true; } " hello, world! "} + => "hello, world! " + trimWith { end = true; } " hello, world! "} + => " hello, world!" + */ + trimWith = + { + # Trim leading whitespace + start ? false, + # Trim trailing whitespace + end ? false, + }: + s: + let + # Define our own whitespace character class instead of using + # `[:space:]`, which is not well-defined. + chars = " \t\r\n"; + + # To match up until trailing whitespace, we need to capture a + # group that ends with a non-whitespace character. + regex = + if start && end then + "[${chars}]*(.*[^${chars}])[${chars}]*" + else if start then + "[${chars}]*(.*)" + else if end then + "(.*[^${chars}])[${chars}]*" + else + "(.*)"; + + # If the string was empty or entirely whitespace, + # then the regex may not match and `res` will be `null`. + res = match regex s; + in + optionalString (res != null) (head res); + /* Construct a Unix-style, colon-separated search path consisting of the given `subDir` appended to each of the given paths. diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 4294d29b47ef..d59f5586b82d 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -369,6 +369,72 @@ runTests { expected = "hellohellohellohellohello"; }; + # Test various strings are trimmed correctly + testTrimString = { + expr = + let + testValues = f: mapAttrs (_: f) { + empty = ""; + cr = "\r"; + lf = "\n"; + tab = "\t"; + spaces = " "; + leading = " Hello, world"; + trailing = "Hello, world "; + mixed = " Hello, world "; + mixed-tabs = " \t\tHello, world \t \t "; + multiline = " Hello,\n world! "; + multiline-crlf = " Hello,\r\n world! "; + }; + in + { + leading = testValues (strings.trimWith { start = true; }); + trailing = testValues (strings.trimWith { end = true; }); + both = testValues strings.trim; + }; + expected = { + leading = { + empty = ""; + cr = ""; + lf = ""; + tab = ""; + spaces = ""; + leading = "Hello, world"; + trailing = "Hello, world "; + mixed = "Hello, world "; + mixed-tabs = "Hello, world \t \t "; + multiline = "Hello,\n world! "; + multiline-crlf = "Hello,\r\n world! "; + }; + trailing = { + empty = ""; + cr = ""; + lf = ""; + tab = ""; + spaces = ""; + leading = " Hello, world"; + trailing = "Hello, world"; + mixed = " Hello, world"; + mixed-tabs = " \t\tHello, world"; + multiline = " Hello,\n world!"; + multiline-crlf = " Hello,\r\n world!"; + }; + both = { + empty = ""; + cr = ""; + lf = ""; + tab = ""; + spaces = ""; + leading = "Hello, world"; + trailing = "Hello, world"; + mixed = "Hello, world"; + mixed-tabs = "Hello, world"; + multiline = "Hello,\n world!"; + multiline-crlf = "Hello,\r\n world!"; + }; + }; + }; + testSplitStringsSimple = { expr = strings.splitString "." "a.b.c.d"; expected = [ "a" "b" "c" "d" ]; |