]> source.dussan.org Git - rspamd.git/commitdiff
[Feature] Add utility to split string like stuff for C++ code
authorVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 17 Aug 2023 10:06:44 +0000 (11:06 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 17 Aug 2023 10:06:44 +0000 (11:06 +0100)
No ranges, as they are a bit ugly to use yet

src/libutil/cxx/util.hxx
src/libutil/cxx/util_tests.cxx

index 9ef2f6295c15b4d98edda179e417392ad92a5aa5..4ec1d568c316bb210314cfd26bb5cf9ea5c4e98f 100644 (file)
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2021 Vsevolod Stakhov
+/*
+ * Copyright 2023 Vsevolod Stakhov
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -53,8 +53,8 @@ constexpr auto find_map(const C &c, const K &k) -> std::optional<std::reference_
 }
 
 
-template<typename _It>
-inline constexpr auto make_string_view_from_it(_It begin, _It end)
+template<typename It>
+inline constexpr auto make_string_view_from_it(It begin, It end)
 {
        using result_type = std::string_view;
 
@@ -91,6 +91,39 @@ inline auto string_foreach_line(const S &input, const F &functor)
        }
 }
 
+/**
+ * Iterate over elements in a string
+ * @tparam S
+ * @tparam F
+ * @param input
+ * @param delim
+ * @param functor
+ * @param ignore_empty
+ * @return
+ */
+template<class S, class D, class F,
+                typename std::enable_if_t<std::is_invocable_v<F, std::string_view> && std::is_constructible_v<std::string_view, S> && std::is_constructible_v<std::string_view, D>, bool> = true>
+inline auto string_foreach_delim(const S &input, const D &delim, const F &functor, const bool ignore_empty = true)
+{
+       size_t first = 0;
+       auto sv_input = std::string_view{input};
+       auto sv_delim = std::string_view{delim};
+
+       while (first < sv_input.size()) {
+               const auto second = sv_input.find_first_of(sv_delim, first);
+
+               if (first != second || !ignore_empty) {
+                       functor(sv_input.substr(first, second - first));
+               }
+
+               if (second == std::string_view::npos) {
+                       break;
+               }
+
+               first = second + 1;
+       }
+}
+
 template<class S, typename std::enable_if_t<std::is_constructible_v<std::string_view, S>, bool> = true>
 inline auto string_split_on(const S &input, std::string_view::value_type chr) -> std::pair<std::string_view, std::string_view>
 {
index 2b309277935f199e3890718bc03e697bee04bba1..83b293ce526850816408195f6ad0a0904e3df11a 100644 (file)
@@ -1,11 +1,11 @@
-/*-
+/*
  * Copyright 2023 Vsevolod Stakhov
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -43,4 +43,39 @@ TEST_SUITE("cxx utils")
                        CHECK(res.second == expected.second);
                }
        }
+
+       TEST_CASE("string_foreach_delim")
+       {
+               std::tuple<std::string_view, std::string_view, std::pair<std::vector<std::string_view>, std::vector<std::string_view>>> cases[] = {
+                       {"test"sv, ","sv, {{"test"}, {"test"}}},
+                       {"test,test"sv, ","sv, {{"test", "test"}, {"test", "test"}}},
+                       {"test, test"sv, ", "sv, {{"test", "test"}, {"test", "", "test"}}},
+                       {"test, test,,"sv, ", "sv, {{"test", "test"}, {"test", "", "test", ""}}},
+               };
+
+               for (const auto &c: cases) {
+                       auto res = std::vector<std::string_view>();
+                       string_foreach_delim(std::get<0>(c), std::get<1>(c), [&](const auto &v) {
+                               res.push_back(v);
+                       });
+
+                       auto compare_vec = []<class T>(const std::vector<T> &v1, const std::vector<T> &v2) {
+                               CHECK(v1.size() == v2.size());
+                               for (size_t i = 0; i < v1.size(); ++i) {
+                                       CHECK(v1[i] == v2[i]);
+                               }
+                       };
+
+                       compare_vec(res, std::get<2>(c).first);
+
+                       res.clear();
+                       // Perform the same test but with no skip empty
+                       string_foreach_delim(
+                               std::get<0>(c), std::get<1>(c), [&](const auto &v) {
+                                       res.push_back(v);
+                               },
+                               false);
+                       compare_vec(res, std::get<2>(c).second);
+               }
+       }
 }
\ No newline at end of file