use regex::{Captures, Match, Regex};
use crate::utils::{build_regex, build_whole_word_pattern};
pub trait PatternCapture<'a> {
fn pattern_captures(&self, pattern: &str, case_insensitive: bool) -> Option<Captures>;
fn pattern_matches_as_vec(&'a self, pattern: &str, case_insensitive: bool, outer: bool) -> Vec<Match>;
fn pattern_matches_vec(&'a self, pattern: &str, case_insensitive: bool) -> Vec<Match<'a>> {
self.pattern_matches_as_vec(pattern, case_insensitive, false)
}
fn pattern_matches_outer(&'a self, pattern: &str, case_insensitive: bool) -> Vec<Match<'a>> {
self.pattern_matches_as_vec(pattern, case_insensitive, true)
}
fn pattern_first_match(&'a self, pattern: &str, case_insensitive: bool) -> Option<Match<'a>>;
fn pattern_last_match(&'a self, pattern: &str, case_insensitive: bool) -> Option<Match> {
let matched_segments = self.pattern_matches_vec(pattern, case_insensitive);
if let Some(last) = matched_segments.last() {
Some(*last)
} else {
None
}
}
fn pattern_first_last_matches(&'a self, pattern: &str, case_insensitive: bool) -> Option<(Match, Match)> {
let matched_segments = self.pattern_matches_vec(pattern, case_insensitive);
if let Some(first) = matched_segments.get(0) {
if let Some(last) = matched_segments.last() {
return Some((*first, *last));
}
}
None
}
fn pattern_first_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
Some(first.start())
} else {
None
}
}
fn pattern_first_end_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
Some(first.end())
} else {
None
}
}
fn pattern_last_start_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
Some(first.start())
} else {
None
}
}
fn pattern_last_index(&'a self, pattern: &str, case_insensitive: bool) -> Option<usize> {
if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
Some(first.end())
} else {
None
}
}
fn count_pattern(&'a self, pattern: &'a str, case_insensitive: bool) -> usize {
self.pattern_matches_vec(pattern, case_insensitive).len()
}
fn count_word(&'a self, word: &'a str, case_insensitive: bool) -> usize {
let pattern = build_whole_word_pattern(word);
self.pattern_matches_vec(&pattern, case_insensitive).len()
}
}
pub fn find_matches_within_haystack<'a>(haystack: &'a str, pattern: &str, case_insensitive: bool, outer: bool) -> (Vec<Match<'a>>, Option<Regex>) {
let mut matched_items: Vec<Match<'a>> = Vec::new();
if let Ok(re) = build_regex(pattern, case_insensitive) {
let mut item_keys: Vec<(&str, usize, usize)> = Vec::new();
for inner_captures in re.captures_iter(haystack) {
for capture_opt in inner_captures.iter() {
if let Some(matched_item) = capture_opt {
let item_str = matched_item.as_str();
let item_key = (item_str, matched_item.start(), matched_item.end());
let is_matched = if outer {
true
} else {
item_keys.contains(&item_key) == false
};
if is_matched {
matched_items.push(matched_item.to_owned());
if !outer {
item_keys.push(item_key);
}
}
if outer {
break;
}
}
}
}
(matched_items, Some(re))
} else {
(matched_items, None)
}
}
impl<'a> PatternCapture<'a> for str {
fn pattern_captures(&self, pattern: &str, case_insensitive: bool) -> Option<Captures> {
if let Ok(re) = build_regex(pattern, case_insensitive) {
re.captures(self)
} else {
None
}
}
fn pattern_matches_as_vec(&'a self, pattern: &str, case_insensitive: bool, outer: bool) -> Vec<Match<'a>> {
let (matched_items, _rgx) = find_matches_within_haystack(self, pattern, case_insensitive, outer);
matched_items
}
fn pattern_first_match(&'a self, pattern: &str, case_insensitive: bool) -> Option<Match<'a>> {
if let Ok(re) = build_regex(pattern, case_insensitive) {
re.find(self)
} else {
None
}
}
}