From da37164630b3b0b582908806153be279336a7b83 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Tue, 3 Mar 2026 01:45:39 +0100 Subject: [PATCH] libutil/strings: add `partitionString` This partitions a string into two parts based on the first occurrence of the separator. This function handles the case when the separator never occur in the target string by returning an optional in the second argument of the pair. Change-Id: I9ad44375dbaa137a4d21d2408bc2ffa34fab62a7 Signed-off-by: Raito Bezarius --- lix/libutil/strings.cc | 10 ++++++ lix/libutil/strings.hh | 9 +++++ tests/unit/libutil/tests.cc | 65 +++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) 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 * --------------------------------------------------------------------------*/