diff --git a/src/tint/utils/string.h b/src/tint/utils/string.h index 897d1a17ec..e7313653c3 100644 --- a/src/tint/utils/string.h +++ b/src/tint/utils/string.h @@ -60,7 +60,14 @@ std::string ToString(const std::variant& value) { /// @param prefix the prefix string /// @returns true iff @p str has the prefix @p prefix inline size_t HasPrefix(std::string_view str, std::string_view prefix) { - return str.compare(0, prefix.size(), prefix) == 0; + return str.length() >= prefix.length() && str.substr(0, prefix.length()) == prefix; +} + +/// @param str the input string +/// @param suffix the suffix string +/// @returns true iff @p str has the suffix @p suffix +inline size_t HasSuffix(std::string_view str, std::string_view suffix) { + return str.length() >= suffix.length() && str.substr(str.length() - suffix.length()) == suffix; } /// @param a the first string @@ -78,6 +85,71 @@ void SuggestAlternatives(std::string_view got, utils::StringStream& ss, std::string_view prefix = ""); +/// @param str the input string +/// @param pred the predicate function +/// @return @p str with characters passing the predicate function @p pred removed from the start of +/// the string. +template +std::string_view TrimLeft(std::string_view str, PREDICATE&& pred) { + while (!str.empty() && pred(str.front())) { + str = str.substr(1); + } + return str; +} + +/// @param str the input string +/// @param pred the predicate function +/// @return @p str with characters passing the predicate function @p pred removed from the end of +/// the string. +template +std::string_view TrimRight(std::string_view str, PREDICATE&& pred) { + while (!str.empty() && pred(str.back())) { + str = str.substr(0, str.length() - 1); + } + return str; +} + +/// @param str the input string +/// @param prefix the prefix to trim from @p str +/// @return @p str with the prefix removed, if @p str has the prefix. +inline std::string_view TrimPrefix(std::string_view str, std::string_view prefix) { + return HasPrefix(str, prefix) ? str.substr(prefix.length()) : str; +} + +/// @param str the input string +/// @param suffix the suffix to trim from @p str +/// @return @p str with the suffix removed, if @p str has the suffix. +inline std::string_view TrimSuffix(std::string_view str, std::string_view suffix) { + return HasSuffix(str, suffix) ? str.substr(0, str.length() - suffix.length()) : str; +} + +/// @param str the input string +/// @param pred the predicate function +/// @return @p str with characters passing the predicate function @p pred removed from the start and +/// end of the string. +template +std::string_view Trim(std::string_view str, PREDICATE&& pred) { + return TrimLeft(TrimRight(str, pred), pred); +} + +/// @param c the character to test +/// @returns true if @p c is one of the following: +/// * space (' ') +/// * form feed ('\f') +/// * line feed ('\n') +/// * carriage return ('\r') +/// * horizontal tab ('\t') +/// * vertical tab ('\v') +inline bool IsSpace(char c) { + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; +} + +/// @param str the input string +/// @return @p str with all whitespace (' ') removed from the start and end of the string. +inline std::string_view TrimSpace(std::string_view str) { + return Trim(str, IsSpace); +} + } // namespace tint::utils #endif // SRC_TINT_UTILS_STRING_H_ diff --git a/src/tint/utils/string_test.cc b/src/tint/utils/string_test.cc index 0f351cff9b..9cf8370a5d 100644 --- a/src/tint/utils/string_test.cc +++ b/src/tint/utils/string_test.cc @@ -47,6 +47,15 @@ TEST(StringTest, HasPrefix) { EXPECT_FALSE(HasPrefix("abc", "b")); } +TEST(StringTest, HasSuffix) { + EXPECT_TRUE(HasSuffix("abc", "c")); + EXPECT_TRUE(HasSuffix("abc", "bc")); + EXPECT_TRUE(HasSuffix("abc", "abc")); + EXPECT_FALSE(HasSuffix("abc", "1abc")); + EXPECT_FALSE(HasSuffix("abc", "ac")); + EXPECT_FALSE(HasSuffix("abc", "b")); +} + TEST(StringTest, Distance) { EXPECT_EQ(Distance("hello world", "hello world"), 0u); EXPECT_EQ(Distance("hello world", "helloworld"), 1u); @@ -75,5 +84,74 @@ Possible values: 'hello world', 'Hello World')"); } } +TEST(StringTest, TrimLeft) { + EXPECT_EQ(TrimLeft("hello world", [](char) { return false; }), "hello world"); + EXPECT_EQ(TrimLeft("hello world", [](char c) { return c == 'h'; }), "ello world"); + EXPECT_EQ(TrimLeft("hello world", [](char c) { return c == 'h' || c == 'e'; }), "llo world"); + EXPECT_EQ(TrimLeft("hello world", [](char c) { return c == 'e'; }), "hello world"); + EXPECT_EQ(TrimLeft("hello world", [](char) { return true; }), ""); + EXPECT_EQ(TrimLeft("", [](char) { return false; }), ""); + EXPECT_EQ(TrimLeft("", [](char) { return true; }), ""); +} + +TEST(StringTest, TrimRight) { + EXPECT_EQ(TrimRight("hello world", [](char) { return false; }), "hello world"); + EXPECT_EQ(TrimRight("hello world", [](char c) { return c == 'd'; }), "hello worl"); + EXPECT_EQ(TrimRight("hello world", [](char c) { return c == 'd' || c == 'l'; }), "hello wor"); + EXPECT_EQ(TrimRight("hello world", [](char c) { return c == 'l'; }), "hello world"); + EXPECT_EQ(TrimRight("hello world", [](char) { return true; }), ""); + EXPECT_EQ(TrimRight("", [](char) { return false; }), ""); + EXPECT_EQ(TrimRight("", [](char) { return true; }), ""); +} + +TEST(StringTest, TrimPrefix) { + EXPECT_EQ(TrimPrefix("abc", "a"), "bc"); + EXPECT_EQ(TrimPrefix("abc", "ab"), "c"); + EXPECT_EQ(TrimPrefix("abc", "abc"), ""); + EXPECT_EQ(TrimPrefix("abc", "abc1"), "abc"); + EXPECT_EQ(TrimPrefix("abc", "ac"), "abc"); + EXPECT_EQ(TrimPrefix("abc", "b"), "abc"); + EXPECT_EQ(TrimPrefix("abc", "c"), "abc"); +} + +TEST(StringTest, TrimSuffix) { + EXPECT_EQ(TrimSuffix("abc", "c"), "ab"); + EXPECT_EQ(TrimSuffix("abc", "bc"), "a"); + EXPECT_EQ(TrimSuffix("abc", "abc"), ""); + EXPECT_EQ(TrimSuffix("abc", "1abc"), "abc"); + EXPECT_EQ(TrimSuffix("abc", "ac"), "abc"); + EXPECT_EQ(TrimSuffix("abc", "b"), "abc"); + EXPECT_EQ(TrimSuffix("abc", "a"), "abc"); +} + +TEST(StringTest, Trim) { + EXPECT_EQ(Trim("hello world", [](char) { return false; }), "hello world"); + EXPECT_EQ(Trim("hello world", [](char c) { return c == 'h'; }), "ello world"); + EXPECT_EQ(Trim("hello world", [](char c) { return c == 'd'; }), "hello worl"); + EXPECT_EQ(Trim("hello world", [](char c) { return c == 'h' || c == 'd'; }), "ello worl"); + EXPECT_EQ(Trim("hello world", [](char) { return true; }), ""); + EXPECT_EQ(Trim("", [](char) { return false; }), ""); + EXPECT_EQ(Trim("", [](char) { return true; }), ""); +} + +TEST(StringTest, IsSpace) { + EXPECT_FALSE(IsSpace('a')); + EXPECT_FALSE(IsSpace('z')); + EXPECT_FALSE(IsSpace('\0')); + EXPECT_TRUE(IsSpace(' ')); + EXPECT_TRUE(IsSpace('\f')); + EXPECT_TRUE(IsSpace('\n')); + EXPECT_TRUE(IsSpace('\r')); + EXPECT_TRUE(IsSpace('\t')); + EXPECT_TRUE(IsSpace('\v')); +} + +TEST(StringTest, TrimSpace) { + EXPECT_EQ(TrimSpace("hello world"), "hello world"); + EXPECT_EQ(TrimSpace(" \t hello world\v\f"), "hello world"); + EXPECT_EQ(TrimSpace("hello \t world"), "hello \t world"); + EXPECT_EQ(TrimSpace(""), ""); +} + } // namespace } // namespace tint::utils