Skip to main content

string_patterns/
pattern_match.rs

1use crate::pattern_cache::with_cached_regex;
2use regex::Error;
3
4/// Core regular expression match methods
5pub trait PatternMatch {
6  /// Apply a regular expression match on the current string
7  /// If the regex doesn't compile it will return an error
8  fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error>;
9
10  /// Apply a regular expression match on the current string with a boolean case_insensitive flag
11  /// NB: If the regex doesn't compile it will return false
12  fn pattern_match(&self, pattern: &str, case_insensitive: bool) -> bool {
13    if let Ok(matched) = self.pattern_match_result(pattern, case_insensitive){
14      matched
15    } else {
16      false
17    }
18  }
19
20  /// if the pattern does not match the source string or the regex fails
21  fn pattern_match_ci(&self, pattern: &str) -> bool {
22    self.pattern_match(pattern, true)
23  }
24
25  /// Simple case-sensitive regex-compatible match method that will return false
26  /// if the pattern does not match the source string or the regex fails
27  fn pattern_match_cs(&self, pattern: &str) -> bool {
28    self.pattern_match(pattern, false)
29  }
30
31}
32
33/// Implement regular expression match methods for any string-like type (&str, String,
34/// &String, `Cow<str>`, etc.). The regex is compiled only once per distinct
35/// (pattern, case_insensitive) pair for the life of the process — see `cached_regex` —
36/// so calling this repeatedly with the same pattern, e.g. once per row of a large batch,
37/// never recompiles after the first call.
38impl<T: AsRef<str>> PatternMatch for T {
39
40  ///
41  /// Simple regex-compatible match method that will return an optional boolean
42  /// - Some(true) means the regex is valid and the string matches
43  /// - Some(false) means the regex is valid and the string does not match
44  /// - None means the regex is not valid and can this not be evaluated
45  /// Only the pattern_match_result needs to be implemented
46  fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
47    with_cached_regex(pattern, case_insensitive, |re| re.is_match(self.as_ref()))
48  }
49}
50
51/// Boolean methods to match a pattern within an array of any string-like type
52impl<T: AsRef<str>> PatternMatch for [T] {
53  /// The regex is only compiled once across all elements, and only once ever per distinct
54  /// (pattern, case_insensitive) pair across all calls, thanks to the shared regex cache.
55  fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
56    with_cached_regex(pattern, case_insensitive, |re| {
57      self.iter().any(|segment| re.is_match(segment.as_ref()))
58    })
59  }
60}
61
62/// Pattern methods for arrays or vectors only, return vectors of booleans matching each input string
63pub trait PatternMatches {
64
65  /// Returns result with a vector of tuples with matched status and string slice
66  /// for an array or vector of strings with a case-insensitive flag
67  /// or an error if the regex does not compile
68  fn pattern_matched_pairs_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<(bool, &str)>, Error>;
69
70
71  /// Return a default vector of paired tuples if the regular expression fails in pattern_matched_pairs or pattern_matches
72  /// This has to implemented separately for all other derived methods returning vectors to work correctly
73  fn pattern_matched_pairs_default(&self) -> Vec<(bool, &str)>;
74
75  /// Returns a vector of tuples with matched status and string slice
76  /// for an array or vector of strings with a case-insensitive flag
77  /// If the regular expression fails all items will be false
78  fn pattern_matched_pairs(&self, pattern: &str, case_insensitive: bool) -> Vec<(bool, &str)> {
79    match self.pattern_matched_pairs_result(pattern, case_insensitive) {
80      Ok(results) => results,
81      Err(_error) => self.pattern_matched_pairs_default()
82    }
83  }
84
85  /// Returns result with a vector of boolean matches for an array or vector of strings with case-insensitive flag
86  /// or an error if the regex does not compile
87  fn pattern_matches_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<bool>, Error> {
88    match self.pattern_matched_pairs_result(pattern, case_insensitive) {
89      Ok(items) => Ok(items.into_iter().map(|(result, _item)| result).collect::<Vec<bool>>()),
90      Err(error) => Err(error)
91    }
92  }
93
94  /// Returns a filtered vector of matched string slices (&str) with case-insensitive flag
95  fn pattern_matches_filtered(&self, pattern: &str, case_insensitive: bool) -> Vec<&str> {
96    self.pattern_matched_pairs(pattern, case_insensitive).into_iter().filter(|(is_matched, _item)| *is_matched).map(|(_is_matched, item)| item).collect()
97  }
98
99  /// Returns a filtered vector of matched string slices (&str) in case-insensitive mode
100  fn pattern_matches_filtered_ci(&self, pattern: &str) -> Vec<&str> {
101    self.pattern_matched_pairs(pattern, true).into_iter().filter(|(is_matched, _item)| *is_matched).map(|(_is_matched, item)| item).collect()
102  }
103
104  /// Returns a filtered vector of matched string slices (&str) in case-sensitive mode
105  fn pattern_matches_filtered_cs(&self, pattern: &str) -> Vec<&str> {
106    self.pattern_matched_pairs(pattern, false).into_iter().filter(|(is_matched, _item)| *is_matched).map(|(_is_matched, item)| item).collect()
107  }
108
109
110  /// Returns vector of boolean matches for an array or vector of strings with case-insensitive flag
111  /// must be reimplemented from pattern_matches_result owing to trait bound constraints on unsized arrays
112  fn pattern_matches(&self, pattern: &str, case_insensitive: bool) -> Vec<bool> {
113    self.pattern_matched_pairs(pattern, case_insensitive).into_iter().map(|(matched, _item)| matched).collect()
114  }
115
116  /// Returns vector of boolean matches for an array or vector of strings in case-insensitive mode
117  fn pattern_matches_ci(&self, pattern: &str) -> Vec<bool> {
118    self.pattern_matches(pattern, true)
119  }
120
121  /// Returns vector of boolean matches for an array or vector of strings in case-sensitive mode
122  fn pattern_matches_cs(&self, pattern: &str) -> Vec<bool> {
123    self.pattern_matches(pattern, false)
124  }
125}
126
127/// Multiple match methods for arrays or vectors of any string-like type
128impl<T: AsRef<str>> PatternMatches for [T] {
129
130  /// Returns an Ok result with a vector of boolean matches for an array or vector of strings with a case-insensitive flag
131  /// and an error only if the regex fails to compile.
132  fn pattern_matched_pairs_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<(bool, &str)>, Error> {
133    with_cached_regex(pattern, case_insensitive, |re| {
134      self.iter().map(|segment| (re.is_match(segment.as_ref()), segment.as_ref())).collect::<Vec<(bool, &str)>>()
135    })
136  }
137
138  // Implement default vector of (bool, &str) results for [T]
139  fn pattern_matched_pairs_default(&self) -> Vec<(bool, &str)> {
140    self.iter().map(|item| (false, item.as_ref())).collect()
141  }
142
143}