1use crate::{enums::StringBounds, utils::{pairs_to_string_bounds, strs_to_string_bounds}, BoundsBuilder, BoundsPosition, CaseMatchMode, CharType, StripCharacters};
2
3pub trait SimpleMatch {
7
8 fn equals_ci(&self, pattern: &str) -> bool;
10
11 fn equals_ci_alphanum(&self, pattern: &str) -> bool;
13
14 fn starts_with_ci(&self, pattern: &str) -> bool;
16
17 fn starts_with_ci_alphanum(&self, pattern: &str) -> bool;
19
20 fn ends_with_ci(&self, pattern: &str) -> bool;
22
23 fn ends_with_ci_alphanum(&self, pattern: &str) -> bool;
25
26 fn contains_ci(&self, pattern: &str) -> bool;
28
29 fn contains_ci_alphanum(&self, pattern: &str) -> bool;
31}
32
33impl SimpleMatch for str {
35
36 fn equals_ci(&self, pattern: &str) -> bool {
38 self.to_lowercase() == pattern.to_lowercase()
39 }
40
41 fn equals_ci_alphanum(&self, pattern: &str) -> bool {
43 self.to_lowercase().strip_non_alphanum() == pattern.to_lowercase().strip_non_alphanum()
44 }
45
46 fn starts_with_ci(&self, pattern: &str) -> bool {
48 self.to_lowercase().starts_with(&pattern.to_lowercase())
49 }
50
51 fn starts_with_ci_alphanum(&self, pattern: &str) -> bool {
53 self.to_lowercase().strip_non_alphanum().starts_with(&pattern.to_lowercase())
54 }
55
56 fn ends_with_ci(&self, pattern: &str) -> bool {
58 self.to_lowercase().ends_with(&pattern.to_lowercase())
59 }
60
61 fn ends_with_ci_alphanum(&self, pattern: &str) -> bool {
63 self.to_lowercase().strip_non_alphanum().ends_with(&pattern.to_lowercase())
64 }
65
66 fn contains_ci(&self, pattern: &str) -> bool {
68 self.to_lowercase().contains(&pattern.to_lowercase())
69 }
70
71 fn contains_ci_alphanum(&self, pattern: &str) -> bool {
73 self.to_lowercase().strip_non_alphanum().contains(&pattern.to_lowercase())
74 }
75}
76
77pub trait MatchOccurrences {
79 fn find_matched_indices(&self, pat: &str) -> Vec<usize>;
82
83 fn find_char_indices(&self, pat: char) -> Vec<usize>;
85}
86
87
88impl MatchOccurrences for str {
89 fn find_matched_indices(&self, pat: &str) -> Vec<usize> {
91 self.match_indices(pat).into_iter().map(|pair| pair.0).collect::<Vec<usize>>()
92 }
93
94 fn find_char_indices(&self, pat: char) -> Vec<usize> {
96 self.match_indices(pat).into_iter().map(|pair| pair.0).collect::<Vec<usize>>()
97 }
98}
99
100
101pub trait SimpleMatchesMany where Self:SimpleMatch {
103
104 fn matched_conditional(&self, pattern_sets: &[StringBounds]) -> Vec<bool>;
106
107 fn contains_conditional(&self, pattern_sets: &[(&str, bool)]) -> Vec<bool> {
109 let pattern_sets: Vec<StringBounds> = pairs_to_string_bounds(pattern_sets, BoundsPosition::Contains);
110 self.matched_conditional(&pattern_sets)
111 }
112
113 fn contains_conditional_ci(&self, patterns: &[&str]) -> Vec<bool> {
115 let pattern_sets: Vec<StringBounds> = strs_to_string_bounds(patterns, CaseMatchMode::Insensitive, BoundsPosition::Contains);
116 self.matched_conditional(&pattern_sets)
117 }
118
119 fn contains_conditional_cs(&self, patterns: &[&str]) -> Vec<bool> {
121 let pattern_sets: Vec<StringBounds> = strs_to_string_bounds(patterns, CaseMatchMode::Sensitive, BoundsPosition::Contains);
122 self.matched_conditional(&pattern_sets)
123 }
124
125}
126
127pub(crate) fn match_bounds_rule(txt: &str, item: &StringBounds) -> bool {
131 let cm = item.case_mode();
132 let ci = item.case_insensitive();
133 let base = if ci {
135 match cm {
136 CaseMatchMode::AlphanumInsensitive => txt.to_lowercase().strip_non_alphanum(),
137 _ => txt.to_lowercase()
138 }
139 } else {
140 txt.to_owned()
141 };
142 let pattern = if ci {
144 item.pattern().to_lowercase()
145 } else {
146 item.pattern().to_owned()
147 };
148 let is_matched = if item.starts_with() {
150 base.starts_with(&pattern)
151 } else if item.ends_with() {
152 base.ends_with(&pattern)
153 } else if item.matches_whole() {
154 base == pattern
155 } else {
156 base.contains(&pattern)
157 } == item.is_positive();
158 is_matched
159}
160
161pub(crate) fn match_bounds_rule_set(txt: &str, item: &StringBounds) -> bool {
165 match item {
166 StringBounds::And(inner_rules) => txt.matched_conditional(&inner_rules).into_iter().all(|result| result),
167 StringBounds::Or(inner_rules) => txt.matched_conditional(&inner_rules).into_iter().any(|result| result),
168 _ => match_bounds_rule(txt, item)
169 }
170}
171
172impl SimpleMatchesMany for str {
173
174 fn matched_conditional(&self, pattern_sets: &[StringBounds]) -> Vec<bool> {
176 let mut matched_items: Vec<bool> = Vec::with_capacity(pattern_sets.len());
177 for item in pattern_sets {
178 matched_items.push(match_bounds_rule_set(self, item));
179 }
180 matched_items
181 }
182}
183
184pub trait SimpleMatchAll where Self:SimpleMatchesMany {
186
187 fn match_all_conditional(&self, pattern_sets: &[StringBounds]) -> bool;
189
190 fn contains_all_conditional(&self, pattern_sets: &[(&str, bool)]) -> bool {
192 let pattern_sets: Vec<StringBounds> = pairs_to_string_bounds(pattern_sets, BoundsPosition::Contains);
193 self.match_all_conditional(&pattern_sets)
194 }
195
196 fn contains_all_conditional_ci(&self, patterns: &[&str]) -> bool {
198 let pattern_sets: Vec<StringBounds> = strs_to_string_bounds(patterns, CaseMatchMode::Insensitive, BoundsPosition::Contains);
199 self.match_all_conditional(&pattern_sets)
200 }
201
202 fn contains_all_conditional_cs(&self, patterns: &[&str]) -> bool {
204 let pattern_sets: Vec<StringBounds> = strs_to_string_bounds(patterns, CaseMatchMode::Sensitive, BoundsPosition::Contains);
205 self.match_all_conditional(&pattern_sets)
206 }
207
208}
209
210impl SimpleMatchAll for str {
211
212 fn match_all_conditional(&self, pattern_sets: &[StringBounds]) -> bool {
214 if pattern_sets.len() > 0 {
216 for item in pattern_sets {
217 if !match_bounds_rule_set(self, item) {
219 return false;
220 }
221 }
222 true
224 } else {
225 false
227 }
228 }
229
230}
231
232pub trait SimpleMatchAny where Self:SimpleMatchesMany {
234
235 fn match_any_conditional(&self, pattern_sets: &[StringBounds]) -> bool;
237
238 fn contains_any_conditional(&self, pattern_sets: &[(&str, bool)]) -> bool {
240 let pattern_sets: Vec<StringBounds> = pairs_to_string_bounds(pattern_sets, BoundsPosition::Contains);
241 self.match_any_conditional(&pattern_sets)
242 }
243
244 fn contains_any_conditional_ci(&self, patterns: &[&str]) -> bool {
246 let pattern_sets: Vec<StringBounds> = strs_to_string_bounds(patterns, CaseMatchMode::Insensitive, BoundsPosition::Contains);
247 self.match_any_conditional(&pattern_sets)
248 }
249
250 fn contains_any_conditional_cs(&self, patterns: &[&str]) -> bool {
252 let pattern_sets: Vec<StringBounds> = strs_to_string_bounds(patterns, CaseMatchMode::Sensitive, BoundsPosition::Contains);
253 self.match_any_conditional(&pattern_sets)
254 }
255
256}
257
258impl SimpleMatchAny for str {
259
260 fn match_any_conditional(&self, pattern_sets: &[StringBounds]) -> bool {
262 for item in pattern_sets {
263 if match_bounds_rule_set(self, item) {
265 return true;
266 }
267 }
268 false
270 }
271
272}
273
274pub trait SimplContainsType where Self:SimpleMatch {
276
277 fn contains_type(&self, char_type: CharType) -> bool;
279
280 fn contains_types(&self, char_types: &[CharType]) -> bool;
282
283 fn starts_with_type(&self, char_type: CharType) -> bool;
285
286 fn starts_with_types(&self, char_types: &[CharType]) -> bool;
288
289 fn ends_with_type(&self, char_type: CharType) -> bool;
291
292 fn ends_with_types(&self, char_types: &[CharType]) -> bool;
294
295}
296
297impl SimplContainsType for str {
299
300 fn contains_type(&self, char_type: CharType) -> bool {
302 self.chars().any(|ch| char_type.is_in_range(&ch))
303 }
304
305 fn contains_types(&self, char_types: &[CharType]) -> bool {
306 self.chars().any(|ch| char_types.into_iter().any(|ct| ct.is_in_range(&ch)))
307 }
308
309 fn starts_with_type(&self, char_type: CharType) -> bool {
311 if let Some(first) = self.chars().nth(0) {
312 char_type.is_in_range(&first)
313 } else {
314 false
315 }
316 }
317
318 fn starts_with_types(&self, char_types: &[CharType]) -> bool {
320 if let Some(first) = self.chars().nth(0) {
321 char_types.into_iter().any(|ct| ct.is_in_range(&first))
322 } else {
323 false
324 }
325 }
326
327 fn ends_with_type(&self, char_type: CharType) -> bool {
329 if let Some(first) = self.chars().last() {
330 char_type.is_in_range(&first)
331 } else {
332 false
333 }
334 }
335
336 fn ends_with_types(&self, char_types: &[CharType]) -> bool {
338 if let Some(first) = self.chars().last() {
339 char_types.into_iter().any(|ct| ct.is_in_range(&first))
340 } else {
341 false
342 }
343 }
344
345
346}
347
348
349pub trait SimpleFilterAll<'a, T> {
351
352 fn filter_all_conditional(&'a self, pattern_sets: &[StringBounds]) -> Vec<T>;
354
355 fn filter_all_rules(&'a self, rules: &BoundsBuilder) -> Vec<T> {
356 self.filter_all_conditional(&rules.as_vec())
357 }
358
359}
360
361impl<'a> SimpleFilterAll<'a, &'a str> for [&str] {
363
364 fn filter_all_conditional(&'a self, pattern_sets: &[StringBounds]) -> Vec<&'a str> {
366 self.into_iter().map(|s| s.to_owned()).filter(|s| s.match_all_conditional(pattern_sets)).collect::<Vec<&'a str>>()
367 }
368
369}
370
371impl<'a> SimpleFilterAll<'a, String> for [String] {
373 fn filter_all_conditional(&'a self, pattern_sets: &[StringBounds]) -> Vec<String> {
375 self.into_iter().filter(|s| s.match_all_conditional(pattern_sets)).map(|s| s.to_owned()).collect::<Vec<String>>()
376 }
377
378}
379
380pub trait SimpleFilterAny<'a, T> {
382
383 fn filter_any_conditional(&'a self, pattern_sets: &[StringBounds]) -> Vec<T>;
385
386 fn filter_any_rules(&'a self, rules: &BoundsBuilder) -> Vec<T> {
387 self.filter_any_conditional(&rules.as_vec())
388 }
389
390}
391
392impl<'a> SimpleFilterAny<'a, &'a str> for [&str] {
394
395 fn filter_any_conditional(&'a self, pattern_sets: &[StringBounds]) -> Vec<&'a str> {
397 self.into_iter().map(|s| s.to_owned()).filter(|s| s.match_any_conditional(pattern_sets)).collect::<Vec<&'a str>>()
398 }
399
400}
401
402impl<'a> SimpleFilterAny<'a, String> for [String] {
404 fn filter_any_conditional(&'a self, pattern_sets: &[StringBounds]) -> Vec<String> {
406 self.into_iter().filter(|s| s.match_any_conditional(pattern_sets)).map(|s| s.to_owned()).collect::<Vec<String>>()
407 }
408
409}