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