/ src / utils / regex_set_replacer.rs
regex_set_replacer.rs
 1  use regex::{Regex, RegexSet};
 2  
 3  #[derive(Debug)]
 4  pub struct RegexSetReplacer {
 5      regex_set: RegexSet,
 6      rules: Vec<(Regex, String)>,
 7  }
 8  
 9  impl RegexSetReplacer {
10      pub fn new(rules: Vec<(Regex, String)>) -> Self {
11          let regex_set = RegexSet::new(rules.iter().map(|(r, _)| r.as_str())).unwrap();
12          Self { regex_set, rules }
13      }
14  
15      pub fn match_and_replace(&self, haystack: &str) -> Option<RegexSetReplacerMatch> {
16          self.regex_set.matches(haystack).iter().next().map(|idx| {
17              let (regex, replacement) = &self.rules[idx];
18              let replacement = regex.replace(haystack, replacement).into_owned();
19              RegexSetReplacerMatch { idx, replacement }
20          })
21      }
22  }
23  
24  #[derive(Debug, Clone, PartialEq, Eq)]
25  pub struct RegexSetReplacerMatch {
26      pub idx: usize,
27      pub replacement: String,
28  }
29  
30  #[cfg(test)]
31  mod tests {
32      use super::*;
33  
34      #[test]
35      fn regex_set_replacer() {
36          let replacer = RegexSetReplacer::new(vec![
37              (Regex::new(r"foo(\d+)").unwrap(), "${1}FOO".into()),
38              (Regex::new(r"bar(\d+)").unwrap(), "${1}BAR".into()),
39          ]);
40  
41          let RegexSetReplacerMatch { idx, replacement } =
42              replacer.match_and_replace("foo42").unwrap();
43          assert_eq!(idx, 0);
44          assert_eq!(replacement, "42FOO");
45  
46          let RegexSetReplacerMatch { idx, replacement } =
47              replacer.match_and_replace("bar1337").unwrap();
48          assert_eq!(idx, 1);
49          assert_eq!(replacement, "1337BAR");
50  
51          assert_eq!(replacer.match_and_replace("test"), None);
52      }
53  }