string_patterns/
pattern_capture.rs1use regex::{Captures, Match, Regex};
2
3use crate::utils::{build_regex, build_whole_word_pattern};
4
5pub trait PatternCapture<'a> {
7
8 fn pattern_captures(&self, pattern: &str, case_insensitive: bool) -> Option<Captures>;
10
11 fn pattern_matches_as_vec(&'a self, pattern: &str, case_insensitive: bool, outer: bool) -> Vec<Match>;
14
15 fn pattern_matches_vec(&'a self, pattern: &str, case_insensitive: bool) -> Vec<Match<'a>> {
18 self.pattern_matches_as_vec(pattern, case_insensitive, false)
19 }
20
21 fn pattern_matches_outer(&'a self, pattern: &str, case_insensitive: bool) -> Vec<Match<'a>> {
24 self.pattern_matches_as_vec(pattern, case_insensitive, true)
25 }
26
27 fn pattern_first_match(&'a self, pattern: &str, case_insensitive: bool) -> Option<Match<'a>>;
30
31 fn pattern_last_match(&'a self, pattern: &str, case_insensitive: bool) -> Option<Match> {
33 let matched_segments = self.pattern_matches_vec(pattern, case_insensitive);
34 matched_segments.last().map(|m| *m)
35 }
36
37 fn pattern_first_last_matches(&'a self, pattern: &str, case_insensitive: bool) -> Option<(Match, Match)> {
40 let matched_segments = self.pattern_matches_vec(pattern, case_insensitive);
41 if let Some(first) = matched_segments.get(0) {
42 if let Some(last) = matched_segments.last() {
43 return Some((*first, *last));
44 }
45 }
46 None
47 }
48
49 fn pattern_first_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
52 if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
53 Some(first.start())
54 } else {
55 None
56 }
57 }
58
59 fn pattern_first_end_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
62 if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
63 Some(first.end())
64 } else {
65 None
66 }
67 }
68
69 fn pattern_last_start_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
72 if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
73 Some(first.start())
74 } else {
75 None
76 }
77 }
78
79 fn pattern_last_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
82 if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
83 Some(first.end())
84 } else {
85 None
86 }
87 }
88
89 fn count_pattern(&'a self, pattern: &'a str, case_insensitive: bool) -> usize {
91 self.pattern_matches_vec(pattern, case_insensitive).len()
92 }
93
94 fn count_word(&'a self, word: &'a str, case_insensitive: bool) -> usize {
96 let pattern = build_whole_word_pattern(word);
97 self.pattern_matches_vec(&pattern, case_insensitive).len()
98 }
99}
100
101
102pub fn find_matches_within_haystack<'a>(haystack: &'a str, pattern: &str, case_insensitive: bool, outer: bool) -> (Vec<Match<'a>>, Option<Regex>) {
107 let mut matched_items: Vec<Match<'a>> = Vec::new();
108 if let Ok(re) = build_regex(pattern, case_insensitive) {
109 let mut item_keys: Vec<(&str, usize, usize)> = Vec::new();
110 for inner_captures in re.captures_iter(haystack) {
111 for capture_opt in inner_captures.iter() {
112 if let Some(matched_item) = capture_opt {
113 let item_str = matched_item.as_str();
114
115 let item_key = (item_str, matched_item.start(), matched_item.end());
116 let is_matched = if outer {
117 true
118 } else {
119 item_keys.contains(&item_key) == false
120 };
121 if is_matched {
122 matched_items.push(matched_item.to_owned());
123 if !outer {
124 item_keys.push(item_key);
125 }
126 }
127 if outer {
129 break;
130 }
131 }
132 }
133 }
134 (matched_items, Some(re))
135 } else {
136 (matched_items, None)
137 }
138}
139
140impl<'a> PatternCapture<'a> for str {
142
143 fn pattern_captures(&self, pattern: &str, case_insensitive: bool) -> Option<Captures> {
145 if let Ok(re) = build_regex(pattern, case_insensitive) {
146 re.captures(self)
147 } else {
148 None
149 }
150 }
151
152 fn pattern_matches_as_vec(&'a self, pattern: &str, case_insensitive: bool, outer: bool) -> Vec<Match<'a>> {
154 let (matched_items, _rgx) = find_matches_within_haystack(self, pattern, case_insensitive, outer);
155 matched_items
156 }
157
158 fn pattern_first_match(&'a self, pattern: &str, case_insensitive: bool) -> Option<Match<'a>> {
162 if let Ok(re) = build_regex(pattern, case_insensitive) {
163 re.find(self)
164 } else {
165 None
166 }
167 }
168
169}
170
171