mimic++ v6
Loading...
Searching...
No Matches
StringMatchers.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024 - 2025.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_MATCHERS_STRING_MATCHERS_HPP
7#define MIMICPP_MATCHERS_STRING_MATCHERS_HPP
8
9#include "mimic++/Fwd.hpp"
10#include "mimic++/String.hpp"
12
13#include <algorithm>
14#include <concepts>
15#include <ranges>
16#include <tuple>
17#include <utility>
18
19namespace mimicpp
20{
26 {
27 } constexpr case_insensitive{};
28}
29
30namespace mimicpp::matches::detail
31{
32 template <string String>
33 [[nodiscard]]
34 constexpr auto make_view(String&& str)
35 {
36 using traits_t = string_traits<std::remove_cvref_t<String>>;
37 return traits_t::view(std::forward<String>(str));
38 }
39
40 template <case_foldable_string String>
41 [[nodiscard]]
42 constexpr auto make_case_folded_string(String&& str)
43 {
44 return std::invoke(
45 string_case_fold_converter<string_char_t<String>>{},
46 detail::make_view(std::forward<String>(str)));
47 }
48
49 template <string Target, string Pattern>
50 constexpr void check_string_compatibility() noexcept
51 {
52 static_assert(
53 std::same_as<
56 "Pattern and target string must have the same character-type.");
57 }
58}
59
61{
110
116 template <string Pattern>
117 [[nodiscard]]
118 constexpr auto eq(Pattern&& pattern)
119 {
120 return PredicateMatcher{
121 []<string T, typename Stored>(T&& target, Stored&& stored) {
122 detail::check_string_compatibility<T, Pattern>();
123
124 return std::ranges::equal(
125 detail::make_view(std::forward<T>(target)),
126 detail::make_view(std::forward<Stored>(stored)));
127 },
128 "is equal to {}",
129 "is not equal to {}",
130 std::make_tuple(std::forward<Pattern>(pattern))};
131 }
132
138 template <case_foldable_string Pattern>
139 [[nodiscard]]
140 constexpr auto eq(Pattern&& pattern, [[maybe_unused]] const case_insensitive_t)
141 {
142 return PredicateMatcher{
143 []<case_foldable_string T, typename Stored>(T&& target, Stored&& stored) {
144 detail::check_string_compatibility<T, Pattern>();
145
146 return std::ranges::equal(
147 detail::make_case_folded_string(std::forward<T>(target)),
148 detail::make_case_folded_string(std::forward<Stored>(stored)));
149 },
150 "is case-insensitively equal to {}",
151 "is case-insensitively not equal to {}",
152 std::make_tuple(std::forward<Pattern>(pattern))};
153 }
154
160 template <string Pattern>
161 [[nodiscard]]
162 constexpr auto starts_with(Pattern&& pattern)
163 {
164 return PredicateMatcher{
165 []<string T, typename Stored>(T&& target, Stored&& stored) {
166 detail::check_string_compatibility<T, Pattern>();
167
168 auto patternView = detail::make_view(std::forward<Stored>(stored));
169 const auto [ignore, patternIter] = std::ranges::mismatch(
170 detail::make_view(std::forward<T>(target)),
171 patternView);
172
173 return patternIter == std::ranges::end(patternView);
174 },
175 "starts with {}",
176 "starts not with {}",
177 std::make_tuple(std::forward<Pattern>(pattern))};
178 }
179
185 template <string Pattern>
186 [[nodiscard]]
187 constexpr auto starts_with(Pattern&& pattern, [[maybe_unused]] const case_insensitive_t)
188 {
189 return PredicateMatcher{
190 []<string T, typename Stored>(T&& target, Stored&& stored) {
191 detail::check_string_compatibility<T, Pattern>();
192
193 auto caseFoldedPattern = detail::make_case_folded_string(std::forward<Stored>(stored));
194 const auto [ignore, patternIter] = std::ranges::mismatch(
195 detail::make_case_folded_string(std::forward<T>(target)),
196 caseFoldedPattern);
197
198 return patternIter == std::ranges::end(caseFoldedPattern);
199 },
200 "case-insensitively starts with {}",
201 "case-insensitively starts not with {}",
202 std::make_tuple(std::forward<Pattern>(pattern))};
203 }
204
210 template <string Pattern>
211 [[nodiscard]]
212 constexpr auto ends_with(Pattern&& pattern)
213 {
214 return PredicateMatcher{
215 []<string T, typename Stored>(T&& target, Stored&& stored) {
216 detail::check_string_compatibility<T, Pattern>();
217
218 auto patternView = detail::make_view(std::forward<Stored>(stored))
219 | std::views::reverse;
220 const auto [ignore, patternIter] = std::ranges::mismatch(
221 detail::make_view(std::forward<T>(target)) | std::views::reverse,
222 patternView);
223
224 return patternIter == std::ranges::end(patternView);
225 },
226 "ends with {}",
227 "ends not with {}",
228 std::make_tuple(std::forward<Pattern>(pattern))};
229 }
230
236 template <string Pattern>
237 [[nodiscard]]
238 constexpr auto ends_with(Pattern&& pattern, [[maybe_unused]] const case_insensitive_t)
239 {
240 return PredicateMatcher{
241 []<string T, typename Stored>(T&& target, Stored&& stored) {
242 detail::check_string_compatibility<T, Pattern>();
243
244 auto caseFoldedPattern = detail::make_case_folded_string(std::forward<Stored>(stored))
245 | std::views::reverse;
246 const auto [ignore, patternIter] = std::ranges::mismatch(
247 detail::make_case_folded_string(std::forward<T>(target)) | std::views::reverse,
248 caseFoldedPattern);
249
250 return patternIter == std::ranges::end(caseFoldedPattern);
251 },
252 "case-insensitively ends with {}",
253 "case-insensitively ends not with {}",
254 std::make_tuple(std::forward<Pattern>(pattern))};
255 }
256
262 template <string Pattern>
263 [[nodiscard]]
264 constexpr auto contains(Pattern&& pattern)
265 {
266 return PredicateMatcher{
267 []<string T, typename Stored>(T&& target, Stored&& stored) {
268 detail::check_string_compatibility<T, Pattern>();
269
270 auto patternView = detail::make_view(std::forward<Stored>(stored));
271 return std::ranges::empty(patternView)
272 || !std::ranges::empty(
273 std::ranges::search(
274 detail::make_view(std::forward<T>(target)),
275 std::move(patternView)));
276 },
277 "contains {}",
278 "contains not {}",
279 std::make_tuple(std::forward<Pattern>(pattern))};
280 }
281
287 template <string Pattern>
288 [[nodiscard]]
289 constexpr auto contains(Pattern&& pattern, [[maybe_unused]] const case_insensitive_t)
290 {
291 return PredicateMatcher{
292 []<string T, typename Stored>(T&& target, Stored&& stored) {
293 detail::check_string_compatibility<T, Pattern>();
294
295 auto patternView = detail::make_case_folded_string(std::forward<Stored>(stored));
296 auto targetView = detail::make_case_folded_string(std::forward<T>(target));
297 return std::ranges::empty(patternView)
298 || !std::ranges::empty(std::ranges::search(targetView, patternView));
299 },
300 "case-insensitively contains {}",
301 "case-insensitively contains not {}",
302 std::make_tuple(std::forward<Pattern>(pattern))};
303 }
304
308}
309
310#endif
Generic matcher and the basic building block of most of the built-in matchers.
Definition GeneralMatchers.hpp:68
Determines, whether the given type supports string normalization.
Definition String.hpp:376
struct mimicpp::case_insensitive_t case_insensitive
constexpr auto eq(Pattern &&pattern)
Tests, whether the target string compares equal to the expected string.
Definition StringMatchers.hpp:118
constexpr auto ends_with(Pattern &&pattern)
Tests, whether the target string ends with the pattern string.
Definition StringMatchers.hpp:212
constexpr auto contains(Pattern &&pattern)
Tests, whether the pattern string is part of the target string.
Definition StringMatchers.hpp:264
constexpr auto starts_with(Pattern &&pattern)
Tests, whether the target string starts with the pattern string.
Definition StringMatchers.hpp:162
typename string_traits< std::remove_cvref_t< T > >::char_t string_char_t
Computes the character type for the given string.
Definition String.hpp:260
Definition StringMatchers.hpp:61
Definition BoostTest.hpp:20
Tag type, used in string matchers.
Definition StringMatchers.hpp:26