1 extern crate onig;
2 
3 use onig::{Captures, Regex, Replacer};
4 use std::borrow::Cow;
5 
6 /// A string, with `$1` refering to the first capture group.
7 struct Dollarified<'a>(&'a str);
8 
9 /// Capture Reference to Captured String
10 ///
11 /// Tries to convert a refernece to a capture to the captured text. If
12 /// the reference isn't a valid numeric capture group then no text is
13 /// returned.
capture_str<'t>(caps: &'t Captures, cap_ref: &str) -> Option<&'t str>14 fn capture_str<'t>(caps: &'t Captures, cap_ref: &str) -> Option<&'t str> {
15     cap_ref.parse::<usize>().ok().and_then(|p| caps.at(p))
16 }
17 
18 impl<'a> Replacer for Dollarified<'a> {
reg_replace(&mut self, caps: &Captures) -> Cow<str>19     fn reg_replace(&mut self, caps: &Captures) -> Cow<str> {
20         let mut replacement = String::new();
21         let mut pattern = self.0;
22         while !pattern.is_empty() {
23             if let Some(position) = pattern.find('$') {
24                 // push up to the replacement
25                 replacement.push_str(&pattern[..position]);
26                 pattern = &pattern[position + 1..];
27 
28                 // find the end of the capture reference
29                 let ref_end = pattern
30                     .find(|c| !char::is_numeric(c))
31                     .unwrap_or(pattern.len());
32 
33                 // push the capture from this capture reference
34                 if let Some(cap) = capture_str(caps, &pattern[..ref_end]) {
35                     replacement.push_str(cap);
36                     pattern = &pattern[ref_end..];
37                 } else {
38                     replacement.push('$');
39                 }
40             } else {
41                 // no replacements left
42                 replacement.push_str(pattern);
43                 break;
44             }
45         }
46         replacement.into()
47     }
48 }
49 
test_with(replacement: &str)50 fn test_with(replacement: &str) {
51     let re = Regex::new(r"(\w+) (\w+)").unwrap();
52     let hay = "well (hello world) to you!";
53     println!(
54         "/{}/{}/ -> {}",
55         &hay,
56         &replacement,
57         re.replace(hay, Dollarified(replacement))
58     );
59 }
60 
main()61 fn main() {
62     test_with("$2 $1");
63     test_with("($2 $1)");
64     test_with("|$2|$1|");
65     test_with("|$0|$2$1");
66     test_with("$$$");
67     test_with("$$$3");
68     test_with("$$2$3");
69     test_with("Literal replacement");
70 }
71