1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use regex::{Match,Captures};

use crate::utils::{build_regex, build_whole_word_pattern};

/// Set of methods to capture groups or match objects derived from Regex::captures.
pub trait PatternCapture {

  /// Yields an option with Regex::Captures as returned from re.captures, Accepts a boolean case_insensitive flag
  fn pattern_captures(&self, pattern: &str, case_insensitive: bool) -> Option<Captures>;

  /// Yields a vector of Match objects with start and end index + the captured string. Accepts a boolean case_insensitive flag
  fn pattern_matches_vec(&self, pattern: &str, case_insensitive: bool) -> Vec<Match>;

  /// Yields an option with first match object if available with a boolean case_insensitive flag
  fn pattern_first_match(&self, pattern: &str, case_insensitive: bool) -> Option<Match>;

  /// Yields an option with last match object if available with a boolean case_insensitive flag
  fn pattern_last_match(&self, pattern: &str, case_insensitive: bool) -> Option<Match>;

  /// returns an option with a pair of match objects
  /// If there is only one match the match objects will have the same indices
  fn pattern_first_last_matches(&self, pattern: &str, case_insensitive: bool) -> Option<(Match, Match)>;

  /// Yields an option with an unsigned integer for the index of the start of the first match
  /// with a boolean case_insensitive flag
  fn pattern_first_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize>;

  /// Yields an option with an unsigned integer for the index of the end of the first match
  /// with a boolean case_insensitive flag
  fn pattern_first_end_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize>;

  /// Yields an option with an unsigned integer for the index of the start of the last match
  /// with a boolean case_insensitive flag
  fn pattern_last_start_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize>;

  /// Yields an option with an unsigned integer for the index of the end of the last match
  /// with a boolean case_insensitive flag
  fn pattern_last_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize>;

  // Counts the number of matches with a boolean case_insensitive flag
  fn count_pattern(&self, pattern: &str, case_insensitive: bool) -> usize;

  // Counts the number of whole words with a boolean case_insensitive flag
  fn count_word(&self, word: &str, case_insensitive: bool) -> usize;
}

impl PatternCapture for str {

  // Yields an option with Regex::Captures as returned from re.captures, Accepts a boolean case_insensitive flag
  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
    }
  }

  /// Yields a vector of Match objects with start and end index + the captured string. Accepts a boolean case_insensitive flag
  fn pattern_matches_vec(&self, pattern: &str, case_insensitive: bool) -> Vec<Match> {
    if let Ok(re) = build_regex(pattern, case_insensitive) {
      let mut matched_items: Vec<Match> = Vec::new();
      for capture in re.captures_iter(self)  {
        for matched_opt in capture.iter() {
          if let Some(matched_item) = matched_opt {
            matched_items.push(matched_item);
          }
        }
      }
      matched_items
    } else {
      vec![]
    }
  }

  /// Yields an option with first match object if available with a boolean case_insensitive flag
  /// As this uses re.find it will be fast than the matching last_match method
  fn pattern_first_match(&self, pattern: &str, case_insensitive: bool) -> Option<Match> {
    if let Ok(re) = build_regex(pattern, case_insensitive) {
      re.find(self)
    } else {
      None
    }
  }

  /// Yields an option with last match object if available with a boolean case_insensitive flag
  fn pattern_last_match(&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
    }
  }

  /// returns an option with a pair of match objects
  /// If there is only one match the match objects will have the same indices
  fn pattern_first_last_matches(&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
  }

  /// Yields an option with an unsigned integer for the index of the start of the last match
  /// with a boolean case_insensitive flag
  fn pattern_first_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize> {
    if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
      Some(first.start())
    } else {
      None
    }
  }

  /// Yields an option with an unsigned integer for the index of the end of the first match
  /// with a boolean case_insensitive flag
  fn pattern_first_end_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize> {
    if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
      Some(first.end())
    } else {
      None
    }
  }

  /// Yields an option with an unsigned integer for the index of the start of the last match
  /// with a boolean case_insensitive flag
  fn pattern_last_start_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize> {
    if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
      Some(first.start())
    } else {
      None
    }
  }

  // Yields an option with an unsigned integer for the index of the end of the last match
  /// with a boolean case_insensitive flag
  fn pattern_last_index(&self, pattern: &str, case_insensitive: bool) -> Option<usize> {
    if let Some(first) = self.pattern_first_match(pattern, case_insensitive) {
      Some(first.end())
    } else {
      None
    }
  }

  // Counts the number of matches with a boolean case_insensitive flag
  fn count_pattern(&self, pattern: &str, case_insensitive: bool) -> usize {
    self.pattern_matches_vec(pattern, case_insensitive).len()
  }

  // Counts the number of matches with a boolean case_insensitive flag
  fn count_word(&self, word: &str, case_insensitive: bool) -> usize {
    let pattern = build_whole_word_pattern(word);
    self.pattern_matches_vec(&pattern, case_insensitive).len()
  }

}