1//! A type that represents the union of a set of regular expressions.
2
3use regex::RegexSet as RxSet;
4use std::cell::Cell;
5
6/// A dynamic set of regular expressions.
7#[derive(Clone, Debug, Default)]
8pub struct RegexSet {
9 items: Vec<String>,
10 /// Whether any of the items in the set was ever matched. The length of this
11 /// vector is exactly the length of `items`.
12 matched: Vec<Cell<bool>>,
13 set: Option<RxSet>,
14 /// Whether we should record matching items in the `matched` vector or not.
15 record_matches: bool,
16}
17
18impl RegexSet {
19 /// Create a new RegexSet
20 pub fn new() -> RegexSet {
21 RegexSet {
22 ..Default::default()
23 }
24 }
25
26 /// Is this set empty?
27 pub fn is_empty(&self) -> bool {
28 self.items.is_empty()
29 }
30
31 /// Insert a new regex into this set.
32 pub fn insert<S>(&mut self, string: S)
33 where
34 S: AsRef<str>,
35 {
36 let string = string.as_ref().to_owned();
37 if string == "*" {
38 warn!("using wildcard patterns (`*`) is no longer considered valid. Use `.*` instead");
39 }
40 self.items.push(string);
41 self.matched.push(Cell::new(false));
42 self.set = None;
43 }
44
45 /// Returns slice of String from its field 'items'
46 pub fn get_items(&self) -> &[String] {
47 &self.items[..]
48 }
49
50 /// Returns an iterator over regexes in the set which didn't match any
51 /// strings yet.
52 pub fn unmatched_items(&self) -> impl Iterator<Item = &String> {
53 self.items.iter().enumerate().filter_map(move |(i, item)| {
54 if !self.record_matches || self.matched[i].get() {
55 return None;
56 }
57
58 Some(item)
59 })
60 }
61
62 /// Construct a RegexSet from the set of entries we've accumulated.
63 ///
64 /// Must be called before calling `matches()`, or it will always return
65 /// false.
66 pub fn build(&mut self, record_matches: bool) {
67 let items = self.items.iter().map(|item| format!("^({})$", item));
68 self.record_matches = record_matches;
69 self.set = match RxSet::new(items) {
70 Ok(x) => Some(x),
71 Err(e) => {
72 warn!("Invalid regex in {:?}: {:?}", self.items, e);
73 None
74 }
75 }
76 }
77
78 /// Does the given `string` match any of the regexes in this set?
79 pub fn matches<S>(&self, string: S) -> bool
80 where
81 S: AsRef<str>,
82 {
83 let s = string.as_ref();
84 let set = match self.set {
85 Some(ref set) => set,
86 None => return false,
87 };
88
89 if !self.record_matches {
90 return set.is_match(s);
91 }
92
93 let matches = set.matches(s);
94 if !matches.matched_any() {
95 return false;
96 }
97 for i in matches.iter() {
98 self.matched[i].set(true);
99 }
100
101 true
102 }
103}
104