#include <fmtmacros.hpp>
#include <cctype>
+#include <iostream>
namespace util {
return result;
}
+std::string
+replace_first(const nonstd::string_view string,
+ const nonstd::string_view from,
+ const nonstd::string_view to)
+{
+ if (from.empty()) {
+ return std::string(string);
+ }
+
+ std::string result;
+ const auto pos = string.find(from);
+ if (pos != nonstd::string_view::npos) {
+ result.append(string.data(), pos);
+ result.append(to.data(), to.length());
+ result.append(string.data() + pos + from.size());
+ } else {
+ result = std::string(string);
+ }
+ return result;
+}
+
std::pair<nonstd::string_view, nonstd::optional<nonstd::string_view>>
split_once(const nonstd::string_view string, const char split_char)
{
nonstd::expected<std::string, std::string>
percent_decode(nonstd::string_view string);
+// Replace the first occurrence of `from` to `to` in `string`.
+std::string replace_first(nonstd::string_view string,
+ nonstd::string_view from,
+ nonstd::string_view to);
+
// Split `string` into two parts using `split_char` as the delimiter. The second
// part will be `nullopt` if there is no `split_char` in `string.`
std::pair<nonstd::string_view, nonstd::optional<nonstd::string_view>>
== "invalid percent-encoded string at position 1: a%0g");
}
+TEST_CASE("util::replace_first")
+{
+ CHECK(util::replace_first("", "", "") == "");
+ CHECK(util::replace_first("x", "", "") == "x");
+ CHECK(util::replace_first("", "x", "") == "");
+ CHECK(util::replace_first("", "", "x") == "");
+ CHECK(util::replace_first("x", "y", "z") == "x");
+ CHECK(util::replace_first("x", "x", "y") == "y");
+ CHECK(util::replace_first("xabcyabcz", "abc", "defdef") == "xdefdefyabcz");
+}
+
TEST_CASE("util::split_once")
{
using nonstd::nullopt;