simple_string_patterns/
bounds_builder.rs

1use crate::{enums::StringBounds, utils::{strs_to_negative_string_bounds, strs_to_string_bounds}, BoundsPosition, CaseMatchMode};
2
3/// Build a set of string matching rules
4#[derive(Debug, Clone)]
5pub struct BoundsBuilder<'a> {
6  string_bounds: Vec<StringBounds<'a>>,
7}
8
9impl<'a> BoundsBuilder<'a> {
10  pub fn new() -> Self {
11    BoundsBuilder {
12      string_bounds: Vec::new()
13    }
14  }
15
16  /// Return a vector of StringBounds enum rules for use with filter_all_conditional()
17  pub fn as_vec(&self) -> Vec<StringBounds<'a>> {
18    self.string_bounds.clone()
19  }
20
21  /// Add a "contains" rule with positive and case-insensitive flags 
22  fn starts_with(&mut self, pattern: &'a str, is_positive: bool, case_insensitive: bool) -> Self {
23    self.string_bounds.push(StringBounds::StartsWith(pattern, is_positive, CaseMatchMode::insensitive(case_insensitive)));
24    self.to_owned()
25  }
26
27  /// Add a "starts_with" rule with a positive flags in case-insensitive mode
28  pub fn starts_with_ci(&mut self, pattern: &'a str, is_positive: bool) -> Self {
29    self.starts_with(pattern, is_positive, true)
30  }
31
32  /// Add a "starts_with" rule with a positive flags in case-insensitive mode evaluating only alphanumeric characters
33  pub fn starts_with_ci_alphanum(&mut self, pattern: &'a str, is_positive: bool) -> Self {
34    self.string_bounds.push(StringBounds::StartsWith(pattern, is_positive, CaseMatchMode::AlphanumInsensitive));
35    self.to_owned()
36  }
37
38  /// Add a "starts_with" rule with a positive flag in case-sensitive mode
39  pub fn starts_with_cs(&mut self, pattern: &'a str, is_positive: bool) -> Self {
40    self.starts_with(pattern, is_positive, false)
41  }
42
43  /// Add a positive "starts_with" rule with a case-insensitive flag
44  pub fn starting_with(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
45    self.starts_with(pattern, true, case_insensitive)
46  }
47
48  /// Add a positive "starts_with" rule in case-insensitive mode
49  pub fn starting_with_ci(&mut self, pattern: &'a str) -> Self {
50    self.starting_with(pattern, true)
51  }
52
53  /// Add a positive "starts_with" rule in case-insensitive mode evaluating only alphanumeric characters
54  pub fn starting_with_ci_alphanum(&mut self, pattern: &'a str) -> Self {
55    self.starts_with_ci_alphanum(pattern, true)
56  }
57
58  /// Add a positive "starts_with" rule in case-sensitive mode
59  pub fn starting_with_cs(&mut self, pattern: &'a str) -> Self {
60    self.starting_with(pattern, false)
61  }
62
63  /// Add a negative "starts_with" rule with a case-insensitive flag
64  pub fn not_starting_with(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
65    self.starts_with(pattern, false, case_insensitive)
66  }
67
68  /// Add a negative "starts_with" rule in case-insensitive mode
69  pub fn not_starting_with_ci(&mut self, pattern: &'a str) -> Self {
70    self.not_starting_with(pattern, true)
71  }
72
73  /// Add a negative "starts_with" rule in case-insensitive mode evaluating only alphanumeric characters
74  pub fn not_starting_with_ci_alphanum(&mut self, pattern: &'a str) -> Self {
75    self.starts_with_ci_alphanum(pattern, false)
76  }
77
78  /// Add a negative "starts_with" rule in case-sensitive mode
79  pub fn not_starting_with_cs(&mut self, pattern: &'a str) -> Self {
80    self.not_starting_with(pattern, false)
81  }
82
83  /// Add a "contains" rule with a positive flag in case-insensitive mode
84  pub fn contains(&mut self, pattern: &'a str, is_positive: bool, case_insensitive: bool) -> Self {
85    let cm = if case_insensitive {
86      CaseMatchMode::Insensitive
87    } else {
88      CaseMatchMode::Sensitive
89    };
90    self.string_bounds.push(StringBounds::Contains(pattern, is_positive, cm));
91    self.to_owned()
92  }
93
94  /// Add a "contains" rule with a positive flags in case-insensitive mode evaluating only alphanumeric characters
95  pub fn contains_ci_alphanum(&mut self, pattern: &'a str, is_positive: bool) -> Self {
96    self.string_bounds.push(StringBounds::Contains(pattern, is_positive, CaseMatchMode::AlphanumInsensitive));
97    self.to_owned()
98  }
99
100  /// Add a "contains" rule with a positive flags in case-insensitive mode
101  pub fn contains_ci(&mut self, pattern: &'a str, is_positive: bool) -> Self {
102    self.contains(pattern, is_positive, true)
103  }
104
105  /// Add a "contains" rule with a positive flags in case-sensitive mode
106  pub fn contains_cs(&mut self, pattern: &'a str, is_positive: bool) -> Self {
107    self.contains(pattern, is_positive, false)
108  }
109
110  /// Add a positive "contains" rule with a case-insensitive flag
111  pub fn containing(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
112    self.contains(pattern, true, case_insensitive)
113  }
114
115  /// Add a positive "contains" rule in case-insensitive mode
116  pub fn containing_ci(&mut self, pattern: &'a str) -> Self {
117    self.containing(pattern, true)
118  }
119
120  /// Add a positive "contains" rule in case-insensitive mode evaluating only alphanumeric characters
121  pub fn containing_ci_alphanum(&mut self, pattern: &'a str) -> Self {
122    self.contains_ci_alphanum(pattern, true)
123  }
124
125  /// Add a positive "contains" rule as true in case-sensitive mode
126  pub fn containing_cs(&mut self, pattern: &'a str) -> Self {
127    self.containing(pattern, false)
128  }
129
130  /// Add a negative "contains" rule with a case-insensitive flag
131  pub fn not_containing(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
132    self.contains(pattern, false, case_insensitive)
133  }
134
135  /// Add a negative "contains" rule in case-insensitive mode
136  pub fn not_containing_ci(&mut self, pattern: &'a str) -> Self {
137    self.not_containing(pattern, true)
138  }
139
140  /// Add a negative "contains" rule in case-insensitive mode evaluating only alphanumeric characters
141  pub fn not_containing_ci_alphanum(&mut self, pattern: &'a str) -> Self {
142    self.contains_ci_alphanum(pattern, false)
143  }
144
145  /// Add a negative "contains" rule in case-sensitive mode
146  pub fn not_containing_cs(&mut self, pattern: &'a str) -> Self {
147    self.not_containing(pattern, false)
148  }
149
150  /// Add an "ends_with" rule with a positive and case-insensitive flags
151  fn ends_with(&mut self, pattern: &'a str, is_positive: bool, case_insensitive: bool) -> Self {
152    let cm = if case_insensitive {
153      CaseMatchMode::Insensitive
154    } else {
155      CaseMatchMode::Sensitive
156    };
157    self.string_bounds.push(StringBounds::EndsWith(pattern, is_positive, cm));
158    self.to_owned()
159  }
160
161  /// Add an "ends_with" rule with a positive flag in case-insensitive mode
162  pub fn ends_with_ci(&mut self, pattern: &'a str, is_positive: bool) -> Self {
163    self.ends_with(pattern, is_positive, true)
164  }
165
166  /// Add a "ends_with" rule with a positive flags in case-insensitive mode evaluating only alphanumeric characters
167  pub fn ends_with_ci_alphanum(&mut self, pattern: &'a str, is_positive: bool) -> Self {
168    self.string_bounds.push(StringBounds::EndsWith(pattern, is_positive, CaseMatchMode::AlphanumInsensitive));
169    self.to_owned()
170  }
171
172  /// Add an "ends_with" rule with a positive flag in case-sensitive mode
173  pub fn ends_with_cs(&mut self, pattern: &'a str, is_positive: bool) -> Self {
174    self.ends_with(pattern, is_positive, false)
175  }
176
177  /// Add a positive "ends_with" rule with a case-insensitive flag
178  pub fn ending_with(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
179    self.ends_with(pattern, true, case_insensitive)
180  }
181
182  /// Add a positive "ends_with" rule in case-insensitive mode
183  pub fn ending_with_ci(&mut self, pattern: &'a str) -> Self {
184    self.ending_with(pattern, true)
185  }
186
187  /// Add a positive "ends_with" rule in case-insensitive mode evaluating only alphanumeric characters
188  pub fn ending_with_ci_alphanum(&mut self, pattern: &'a str) -> Self {
189    self.ends_with_ci_alphanum(pattern, true)
190  }
191
192  /// Add a positive "ends_with" rule in case-sensitive mode
193  pub fn ending_with_cs(&mut self, pattern: &'a str) -> Self {
194    self.ending_with(pattern, false)
195  }
196
197  /// Add a negative "ends_with" rule  with a case-insensitive flag
198  pub fn not_ending_with(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
199    self.ends_with(pattern, false, case_insensitive)
200  }
201
202  /// Add a negative "ends_with" rule in case-insensitive mode
203  pub fn not_ending_with_ci(&mut self, pattern: &'a str) -> Self {
204    self.not_ending_with(pattern, true)
205  }
206  
207  /// Add a negative "ends_with" rule in case-insensitive mode evaluating only alphanumeric characters
208  pub fn not_ending_with_ci_alphanum(&mut self, pattern: &'a str) -> Self {
209    self.ends_with_ci_alphanum(pattern, false)
210  }
211
212  /// Add a negative "ends_with" in case-sensitive mode
213  pub fn not_ending_with_cs(&mut self, pattern: &'a str) -> Self {
214    self.not_ending_with(pattern, false)
215  }
216
217  /// Add an "whole_match" rule with a positive and case-insensitive flags
218  pub fn matches_whole(&mut self, pattern: &'a str, is_positive: bool, case_insensitive: bool) -> Self {
219    let cm = if case_insensitive {
220      CaseMatchMode::Insensitive
221    } else {
222      CaseMatchMode::Sensitive
223    };
224    self.string_bounds.push(StringBounds::Whole(pattern, is_positive, cm));
225    self.to_owned()
226  }
227
228  /// Add an "whole_match" rule with a positive flag in case-sensitive mode
229  pub fn matches_whole_ci(&mut self, pattern: &'a str, is_positive: bool) -> Self {
230    self.matches_whole(pattern, is_positive, true)
231  }
232
233  /// Add an "whole_match" rule with a positive flag in case-insensitive mode
234  pub fn matches_whole_cs(&mut self, pattern: &'a str, is_positive: bool) -> Self {
235    self.matches_whole(pattern, is_positive, false)
236  }
237
238  pub fn is(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
239    self.matches_whole(pattern, true, case_insensitive)
240  }
241
242  pub fn is_ci(&mut self, pattern: &'a str) -> Self {
243    self.matches_whole(pattern, true, true)
244  }
245
246  pub fn is_not(&mut self, pattern: &'a str, case_insensitive: bool) -> Self {
247    self.matches_whole(pattern, false, case_insensitive)
248  }
249
250  pub fn is_not_ci(&mut self, pattern: &'a str) -> Self {
251    self.matches_whole(pattern, false, true)
252  }
253
254  pub fn is_not_cs(&mut self, pattern: &'a str) -> Self {
255    self.matches_whole(pattern, false, false)
256  }
257
258  // Add a rule set defined via bounds_builder() with ann logic
259  // All must match to return true
260  pub fn and(&mut self, rules: BoundsBuilder<'a>) -> Self {
261    self.string_bounds.push(StringBounds::And(rules.as_vec()));
262    self.to_owned()
263  }
264
265  // Add a rule set defined via bounds_builder() with or logic
266  // Only one need match to return true
267  pub fn or(&mut self, rules: BoundsBuilder<'a>) -> Self {
268    self.string_bounds.push(StringBounds::Or(rules.as_vec()));
269    self.to_owned()
270  }
271
272  // any of an array of patterns with the same case match mode and position need match
273  // usually defined via wrapper with descriptive names and a single patterns parameter, e.g. or_starting_with_ci()
274  pub fn or_true(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode, position: BoundsPosition) -> Self {
275    let bounds: Vec<StringBounds<'a>> = strs_to_string_bounds(patterns, case_mode, position);
276    self.string_bounds.push(StringBounds::Or(bounds));
277    self.to_owned()
278  }
279
280  pub fn or_starts_with(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
281    self.or_true(patterns, case_mode, BoundsPosition::Starts);
282    self.to_owned()
283  }
284
285  pub fn or_starting_with_ci(&mut self, patterns: &'a [&str]) -> Self {
286    self.or_starts_with(patterns, CaseMatchMode::Insensitive);
287    self.to_owned()
288  }
289
290  pub fn or_starting_with_cs(&mut self, patterns: &'a [&str]) -> Self {
291    self.or_starts_with(patterns, CaseMatchMode::Sensitive);
292    self.to_owned()
293  }
294
295  pub fn or_starting_with_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
296    self.or_starts_with(patterns, CaseMatchMode::AlphanumInsensitive);
297    self.to_owned()
298  }
299
300  pub fn or_contains(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
301    self.or_true(patterns, case_mode, BoundsPosition::Contains);
302    self.to_owned()
303  }
304
305  pub fn or_containing_ci(&mut self, patterns: &'a [&str]) -> Self {
306    self.or_contains(patterns, CaseMatchMode::Insensitive);
307    self.to_owned()
308  }
309
310  pub fn or_containing_cs(&mut self, patterns: &'a [&str]) -> Self {
311    self.or_contains(patterns, CaseMatchMode::Sensitive);
312    self.to_owned()
313  }
314
315  pub fn or_containing_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
316    self.or_contains(patterns, CaseMatchMode::AlphanumInsensitive);
317    self.to_owned()
318  }
319
320  pub fn or_ends_with(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
321    self.or_true(patterns, case_mode, BoundsPosition::Ends);
322    self.to_owned()
323  }
324
325  pub fn or_ending_with_ci(&mut self, patterns: &'a [&str]) -> Self {
326    self.or_ends_with(patterns, CaseMatchMode::Insensitive);
327    self.to_owned()
328  }
329
330  pub fn or_ending_with_cs(&mut self, patterns: &'a [&str]) -> Self {
331    self.or_ends_with(patterns, CaseMatchMode::Sensitive);
332    self.to_owned()
333  }
334
335  pub fn or_ending_with_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
336    self.or_ends_with(patterns, CaseMatchMode::AlphanumInsensitive);
337    self.to_owned()
338  }
339
340  pub fn or_is(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
341    self.or_true(patterns, case_mode, BoundsPosition::Whole);
342    self.to_owned()
343  }
344
345  pub fn or_is_ci(&mut self, patterns: &'a [&str]) -> Self {
346    self.or_is(patterns, CaseMatchMode::Insensitive);
347    self.to_owned()
348  }
349
350  pub fn or_is_cs(&mut self, patterns: &'a [&str]) -> Self {
351    self.or_is(patterns, CaseMatchMode::Sensitive);
352    self.to_owned()
353  }
354
355  pub fn or_is_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
356    self.or_is(patterns, CaseMatchMode::AlphanumInsensitive);
357    self.to_owned()
358  }
359
360  // all within an array of patterns with the same case match mode and position must match
361  // usually defined via wrapper with descriptive names and a single patterns parameter, e.g. and_starting_with_ci()
362  pub fn and_true(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode, position: BoundsPosition) -> Self {
363    let bounds: Vec<StringBounds<'a>> = strs_to_string_bounds(patterns, case_mode, position);
364    self.string_bounds.push(StringBounds::And(bounds));
365    self.to_owned()
366  }
367
368  // all within an array of patterns with the same case match mode and position must not match
369  // usually defined via wrapper with descriptive names and a single patterns parameter, e.g. and_not_starting_with_ci()
370  pub fn and_false(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode, position: BoundsPosition) -> Self {
371    let bounds: Vec<StringBounds<'a>> = strs_to_negative_string_bounds(patterns, case_mode, position);
372    self.string_bounds.push(StringBounds::And(bounds));
373    self.to_owned()
374  }
375
376  pub fn and_starts_with(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
377    self.and_true(patterns, case_mode, BoundsPosition::Starts);
378    self.to_owned()
379  }
380
381  pub fn and_not_starts_with(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
382    self.and_false(patterns, case_mode, BoundsPosition::Starts);
383    self.to_owned()
384  }
385
386  pub fn and_starting_with_ci(&mut self, patterns: &'a [&str]) -> Self {
387    self.and_not_starts_with(patterns, CaseMatchMode::Insensitive);
388    self.to_owned()
389  }
390
391  pub fn and_not_starting_with_ci(&mut self, patterns: &'a [&str]) -> Self {
392    self.and_not_starts_with(patterns, CaseMatchMode::Insensitive);
393    self.to_owned()
394  }
395
396  pub fn and_starting_with_cs(&mut self, patterns: &'a [&str]) -> Self {
397    self.and_starts_with(patterns, CaseMatchMode::Sensitive);
398    self.to_owned()
399  }
400
401  pub fn and_not_starting_with_cs(&mut self, patterns: &'a [&str]) -> Self {
402    self.and_not_starts_with(patterns, CaseMatchMode::Sensitive);
403    self.to_owned()
404  }
405
406  pub fn and_starting_with_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
407    self.and_starts_with(patterns, CaseMatchMode::AlphanumInsensitive);
408    self.to_owned()
409  }
410
411  pub fn and_not_starting_with_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
412    self.and_not_starts_with(patterns, CaseMatchMode::AlphanumInsensitive);
413    self.to_owned()
414  }
415
416  pub fn and_contains(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
417    self.and_true(patterns, case_mode, BoundsPosition::Contains);
418    self.to_owned()
419  }
420
421  pub fn and_not_contains(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
422    self.and_false(patterns, case_mode, BoundsPosition::Contains);
423    self.to_owned()
424  }
425
426  pub fn and_containing_ci(&mut self, patterns: &'a [&str]) -> Self {
427    self.and_contains(patterns, CaseMatchMode::Insensitive);
428    self.to_owned()
429  }
430
431  pub fn and_not_containing_ci(&mut self, patterns: &'a [&str]) -> Self {
432    self.and_not_contains(patterns, CaseMatchMode::Insensitive);
433    self.to_owned()
434  }
435
436  pub fn and_containing_cs(&mut self, patterns: &'a [&str]) -> Self {
437    self.and_contains(patterns, CaseMatchMode::Sensitive);
438    self.to_owned()
439  }
440
441  pub fn and_not_containing_cs(&mut self, patterns: &'a [&str]) -> Self {
442    self.and_not_contains(patterns, CaseMatchMode::Sensitive);
443    self.to_owned()
444  }
445
446  pub fn and_containing_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
447    self.and_contains(patterns, CaseMatchMode::AlphanumInsensitive);
448    self.to_owned()
449  }
450
451  pub fn and_not_containing_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
452    self.and_not_contains(patterns, CaseMatchMode::AlphanumInsensitive);
453    self.to_owned()
454  }
455
456  pub fn and_ends_with(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
457    self.and_true(patterns, case_mode, BoundsPosition::Ends);
458    self.to_owned()
459  }
460
461  pub fn and_not_ends_with(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
462    self.and_false(patterns, case_mode, BoundsPosition::Ends);
463    self.to_owned()
464  }
465
466  pub fn and_ending_with_ci(&mut self, patterns: &'a [&str]) -> Self {
467    self.and_ends_with(patterns, CaseMatchMode::Insensitive);
468    self.to_owned()
469  }
470
471  pub fn and_not_ending_with_ci(&mut self, patterns: &'a [&str]) -> Self {
472    self.and_not_ends_with(patterns, CaseMatchMode::Insensitive);
473    self.to_owned()
474  }
475
476  pub fn and_ending_with_cs(&mut self, patterns: &'a [&str]) -> Self {
477    self.and_ends_with(patterns, CaseMatchMode::Sensitive);
478    self.to_owned()
479  }
480
481  pub fn and_not_ending_with_cs(&mut self, patterns: &'a [&str]) -> Self {
482    self.and_not_ends_with(patterns, CaseMatchMode::Sensitive);
483    self.to_owned()
484  }
485
486  pub fn and_ending_with_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
487    self.and_ends_with(patterns, CaseMatchMode::AlphanumInsensitive);
488    self.to_owned()
489  }
490
491  pub fn and_not_ending_with_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
492    self.and_not_ends_with(patterns, CaseMatchMode::AlphanumInsensitive);
493    self.to_owned()
494  }
495
496  pub fn and_is(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
497    self.and_true(patterns, case_mode, BoundsPosition::Whole);
498    self.to_owned()
499  }
500
501  pub fn and_is_not(&mut self, patterns: &'a [&str], case_mode: CaseMatchMode) -> Self {
502    self.and_false(patterns, case_mode, BoundsPosition::Whole);
503    self.to_owned()
504  }
505
506  pub fn and_is_ci(&mut self, patterns: &'a [&str]) -> Self {
507    self.and_is(patterns, CaseMatchMode::Insensitive);
508    self.to_owned()
509  }
510
511  pub fn and_is_not_ci(&mut self, patterns: &'a [&str]) -> Self {
512    self.and_is_not(patterns, CaseMatchMode::Insensitive);
513    self.to_owned()
514  }
515
516  pub fn and_is_cs(&mut self, patterns: &'a [&str]) -> Self {
517    self.and_is(patterns, CaseMatchMode::Sensitive);
518    self.to_owned()
519  }
520
521  pub fn and_is_not_cs(&mut self, patterns: &'a [&str]) -> Self {
522    self.and_is_not(patterns, CaseMatchMode::Sensitive);
523    self.to_owned()
524  }
525
526  pub fn and_is_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
527    self.and_is(patterns, CaseMatchMode::AlphanumInsensitive);
528    self.to_owned()
529  }
530
531  pub fn and_is_not_ci_alphanum(&mut self, patterns: &'a [&str]) -> Self {
532    self.and_is_not(patterns, CaseMatchMode::AlphanumInsensitive);
533    self.to_owned()
534  }
535
536}
537
538/// Convenience method to build rule-sets
539/// This starts a new BoundBuilder object with chained rule sets
540pub fn bounds_builder<'a>() -> BoundsBuilder<'a> {
541  BoundsBuilder::new()
542}