• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(Clone, 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     /// Create a new RegexSet
new() -> RegexSet20     pub fn new() -> RegexSet {
21         RegexSet {
22             ..Default::default()
23         }
24     }
25 
26     /// Is this set empty?
is_empty(&self) -> bool27     pub fn is_empty(&self) -> bool {
28         self.items.is_empty()
29     }
30 
31     /// Insert a new regex into this set.
insert<S>(&mut self, string: S) where S: AsRef<str>,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'
get_items(&self) -> &[String]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.
unmatched_items(&self) -> impl Iterator<Item = &String>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.
build(&mut self, record_matches: bool)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?
matches<S>(&self, string: S) -> bool where S: AsRef<str>,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