string_patterns/
pattern_match.rs

1use regex::Error;
2use crate::utils::build_regex;
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 and replace methods for str and owned String
34impl PatternMatch for str {
35
36  ///
37  /// Simple regex-compatible match method that will return an optional boolean 
38  /// - Some(true) means the regex is valid and the string matches
39  /// - Some(false) means the regex is valid and the string does not match
40  /// - None means the regex is not valid and can this not be evaluated
41  /// Only the pattern_match_result needs to be implemented
42  fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
43    match build_regex(pattern, case_insensitive) {
44      Ok(re)  => Ok(re.is_match(self)),
45      Err(error) => Err(error)
46    }
47  }
48}
49
50/// Boolean methods to match a pattern within an array of strings
51impl PatternMatch for [&str] {
52  /// The regex is only compiled when validating an array of strings
53  fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
54    match build_regex(pattern, case_insensitive) {
55      Ok(re) => Ok(self.into_iter().any(|segment| re.is_match(*segment))),
56      Err(error) => Err(error)
57    }
58  }
59}
60
61/// Boolean methods to match a pattern within an array of strings
62impl PatternMatch for [String] {
63  /// The regex is only compiled when validating an array of strings
64  fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
65    match build_regex(pattern, case_insensitive) {
66      Ok(re) => Ok(self.into_iter().any(|segment| re.is_match(segment))),
67      Err(error) => Err(error)
68    }
69  }
70}
71
72/// Pattern methods for arrays or vectors only, return vectors of booleans matching each input string
73pub trait PatternMatches {
74
75  /// Returns result with a vector of tuples with matched status and string slice
76  /// for an array or vector of strings with a case-insensitive flag
77  /// or an error if the regex does not compile
78  fn pattern_matched_pairs_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<(bool, &str)>, Error>;
79
80
81  /// Return a default vector of paired tuples if the regular expression fails in pattern_matched_pairs or pattern_matches
82  /// This has to implemented separately for all other derived methods returning vectors to work correctly
83  fn pattern_matched_pairs_default(&self) -> Vec<(bool, &str)>;
84
85  /// Returns a vector of tuples with matched status and string slice
86  /// for an array or vector of strings with a case-insensitive flag
87  /// If the regular expression fails all items will be false
88  fn pattern_matched_pairs(&self, pattern: &str, case_insensitive: bool) -> Vec<(bool, &str)> {
89    match self.pattern_matched_pairs_result(pattern, case_insensitive) {
90      Ok(results) => results,
91      Err(_error) => self.pattern_matched_pairs_default()
92    }
93  }
94
95  /// Returns result with a vector of boolean matches for an array or vector of strings with case-insensitive flag
96  /// or an error if the regex does not compile
97  fn pattern_matches_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<bool>, Error> {
98    match self.pattern_matched_pairs_result(pattern, case_insensitive) {
99      Ok(items) => Ok(items.into_iter().map(|(result, _item)| result).collect::<Vec<bool>>()),
100      Err(error) => Err(error)
101    }
102  }
103
104  /// Returns a filtered vector of matched string slices (&str) with case-insensitive flag
105  fn pattern_matches_filtered(&self, pattern: &str, case_insensitive: bool) -> Vec<&str> {
106    self.pattern_matched_pairs(pattern, case_insensitive).into_iter().filter(|(is_matched, _item)| *is_matched).map(|(_is_matched, item)| item).collect()
107  }
108
109  /// Returns a filtered vector of matched string slices (&str) in case-insensitive mode
110  fn pattern_matches_filtered_ci(&self, pattern: &str) -> Vec<&str> {
111    self.pattern_matched_pairs(pattern, true).into_iter().filter(|(is_matched, _item)| *is_matched).map(|(_is_matched, item)| item).collect()
112  }
113
114  /// Returns a filtered vector of matched string slices (&str) in case-sensitive mode
115  fn pattern_matches_filtered_cs(&self, pattern: &str) -> Vec<&str> {
116    self.pattern_matched_pairs(pattern, false).into_iter().filter(|(is_matched, _item)| *is_matched).map(|(_is_matched, item)| item).collect()
117  }
118
119
120  /// Returns vector of boolean matches for an array or vector of strings with case-insensitive flag
121  /// must be reimplemented from pattern_matches_result owing to trait bound constraints on unsized arrays
122  fn pattern_matches(&self, pattern: &str, case_insensitive: bool) -> Vec<bool> {
123    self.pattern_matched_pairs(pattern, case_insensitive).into_iter().map(|(matched, _item)| matched).collect()
124  }
125  
126  /// Returns vector of boolean matches for an array or vector of strings in case-insensitive mode
127  fn pattern_matches_ci(&self, pattern: &str) -> Vec<bool> {
128    self.pattern_matches(pattern, true)
129  }
130
131  /// Returns vector of boolean matches for an array or vector of strings in case-sensitive mode
132  fn pattern_matches_cs(&self, pattern: &str) -> Vec<bool> {
133    self.pattern_matches(pattern, false)
134  }
135}
136
137/// Multiple match methods for arrays or vectors of &str values
138impl PatternMatches for [&str] {
139
140  /// Returns an Ok result with a vector of boolean matches for an array or vector of strings with a case-insensitive flag
141  /// and an error only if the regex fails to compile.
142  fn pattern_matched_pairs_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<(bool, &str)>, Error> {
143    match build_regex(pattern, case_insensitive) {
144      Ok(re) => Ok(self.into_iter().map(|segment| (re.is_match(*segment), *segment)).collect::<Vec<(bool, &str)>>()),
145      Err(error) => Err(error)
146    }
147  }
148
149  // Implement default vector of (bool, &str) results for [&str]
150  fn pattern_matched_pairs_default(&self) -> Vec<(bool, &str)> {
151    self.into_iter().map(|item| (false, *item)).collect()
152  }
153
154}
155
156/// Multiple match methods for arrays or vectors of strings
157/// Implemented separately because of irresolvable Iterator trait bounds rules in [Rust 2018](https://doc.rust-lang.org/stable/edition-guide/rust-2021/IntoIterator-for-arrays.html)
158/// and because both String and &str are normalised to vectors with string references in the return types
159impl PatternMatches for [String] {
160
161  /// Returns an Ok result with a vector of boolean matches for an array or vector of strings with a case-insensitive flag
162  /// and an error only if the regex fails to compile.
163  fn pattern_matched_pairs_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<(bool, &str)>, Error> {
164    match build_regex(pattern, case_insensitive) {
165      Ok(re) => Ok(self.into_iter().map(|segment| (re.is_match(segment), segment.as_str())).collect::<Vec<(bool, &str)>>()),
166      Err(error) => Err(error)
167    }
168  }
169
170  // Implement default vector of (bool, &str) results for [String]
171  fn pattern_matched_pairs_default(&self) -> Vec<(bool, &str)> {
172    self.into_iter().map(|item| (false, item.as_str())).collect()
173  }
174  
175}