diff --git a/lix/libutil/strings.cc b/lix/libutil/strings.cc index 9eb2842b0..914e6e78e 100644 --- a/lix/libutil/strings.cc +++ b/lix/libutil/strings.cc @@ -36,6 +36,16 @@ template Strings tokenizeString(std::string_view s, std::string_view separators) template StringSet tokenizeString(std::string_view s, std::string_view separators); template std::vector tokenizeString(std::string_view s, std::string_view separators); +std::pair> +partitionString(std::string_view s, char separator) +{ + auto pos = s.find_first_of(separator); + if (pos == std::string::npos) { + return {s, std::nullopt}; + } else { + return {s.substr(0, pos), s.substr(pos + 1)}; + } +} std::string chomp(std::string_view s) { diff --git a/lix/libutil/strings.hh b/lix/libutil/strings.hh index 03f32ce14..51a78f238 100644 --- a/lix/libutil/strings.hh +++ b/lix/libutil/strings.hh @@ -35,6 +35,15 @@ MakeError(FormatError, Error); */ template C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); +/** + * Returns a pair that partitions the string when the separator is encountered for the first time in the + * string. e.g. `a=b` with separator `=` would split into {a, b} If the string passed does not contain the + * separator, the returned pair will set the second element to nullopt and put the whole string in the first + * element. + */ +std::pair> +partitionString(std::string_view s, char separator); + /** * Concatenate the given strings with a separator between the * elements. diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 24ddbb56f..df7b7f7cc 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -729,6 +729,71 @@ namespace nix { ASSERT_EQ(tokenizeString(s, ","), expected); } + /* ---------------------------------------------------------------------------- + * partitionString + * --------------------------------------------------------------------------*/ + + TEST(partitionString, emptyString) + { + auto result = partitionString("", '='); + + ASSERT_EQ(result.first, ""); + ASSERT_FALSE(result.second.has_value()); + } + + TEST(partitionString, noSeparator) + { + auto result = partitionString("foobar", '='); + + ASSERT_EQ(result.first, "foobar"); + ASSERT_FALSE(result.second.has_value()); + } + + TEST(partitionString, separatorInMiddle) + { + auto result = partitionString("a=b", '='); + + ASSERT_EQ(result.first, "a"); + ASSERT_TRUE(result.second.has_value()); + ASSERT_EQ(result.second.value(), "b"); + } + + TEST(partitionString, separatorAtBeginning) + { + auto result = partitionString("=abc", '='); + + ASSERT_EQ(result.first, ""); + ASSERT_TRUE(result.second.has_value()); + ASSERT_EQ(result.second.value(), "abc"); + } + + TEST(partitionString, separatorAtEnd) + { + auto result = partitionString("abc=", '='); + + ASSERT_EQ(result.first, "abc"); + ASSERT_TRUE(result.second.has_value()); + ASSERT_EQ(result.second.value(), ""); + } + + TEST(partitionString, multipleSeparatorsOnlyFirstIsUsed) + { + auto result = partitionString("a=b=c", '='); + + ASSERT_EQ(result.first, "a"); + ASSERT_TRUE(result.second.has_value()); + ASSERT_EQ(result.second.value(), "b=c"); + } + + TEST(partitionString, differentSeparator) + { + auto result = partitionString("key:value", ':'); + + ASSERT_EQ(result.first, "key"); + ASSERT_TRUE(result.second.has_value()); + ASSERT_EQ(result.second.value(), "value"); + } + /* ---------------------------------------------------------------------------- * get * --------------------------------------------------------------------------*/