summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Sturgeon <matt@sturgeon.me.uk>2024-05-28 18:22:43 +0100
committerMatt Sturgeon <matt@sturgeon.me.uk>2024-07-25 23:43:33 +0100
commitaad87c2aa8d00fe8a9c4d8198cf166d652f8f28c (patch)
tree393fab90dfcd7380d2d231fdbeed75d55e6547f5
parentMerge pull request #329788 from r-ryantm/auto-update/ants (diff)
downloadnixpkgs-aad87c2aa8d00fe8a9c4d8198cf166d652f8f28c.tar.gz
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.
-rw-r--r--lib/default.nix2
-rw-r--r--lib/strings.nix62
-rw-r--r--lib/tests/misc.nix66
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" ];