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