1 use super::{Captures, Regex};
2 use std::borrow::Cow;
3 
4 /// Replacer describes types that can be used to replace matches in a string.
5 ///
6 /// Implementations are provided for replacement using string literals
7 /// and `FnMut` callbacks. If this isn't enough for your replacement
8 /// needs a user-supplied `Replacer` implemenation can be
9 /// provided. For an example of a custom replacer implementation check
10 /// out `examples/dollar.rs` in the Onig crate.
11 pub trait Replacer {
12     /// Returns a possibly owned string that is used to replace the match
13     /// corresponding to the `caps` capture group.
reg_replace(&mut self, caps: &Captures) -> Cow<str>14     fn reg_replace(&mut self, caps: &Captures) -> Cow<str>;
15 }
16 
17 /// Replacement using Literal Strings
18 impl<'t> Replacer for &'t str {
reg_replace(&mut self, _: &Captures) -> Cow<str>19     fn reg_replace(&mut self, _: &Captures) -> Cow<str> {
20         (*self).into()
21     }
22 }
23 
24 /// Replacement using `FnMut` Callbacks
25 impl<F> Replacer for F
26 where
27     F: FnMut(&Captures) -> String,
28 {
reg_replace<'a>(&'a mut self, caps: &Captures) -> Cow<'a, str>29     fn reg_replace<'a>(&'a mut self, caps: &Captures) -> Cow<'a, str> {
30         (*self)(caps).into()
31     }
32 }
33 
34 impl Regex {
35     /// Replaces the leftmost-first match with the replacement provided.
36     /// The replacement can be a regular string or a function that takes
37     /// the matches `Captures` and returns the replaced string.
38     ///
39     /// If no match is found, then a copy of the string is returned unchanged.
40     ///
41     /// # Examples
42     ///
43     /// Note that this function is polymorphic with respect to the replacement.
44     /// In typical usage, this can just be a normal string:
45     ///
46     /// ```rust
47     /// # extern crate onig; use onig::Regex;
48     /// # fn main() {
49     /// let re = Regex::new("[^01]+").unwrap();
50     /// assert_eq!(re.replace("1078910", ""), "1010");
51     /// # }
52     /// ```
53     ///
54     /// But anything satisfying the `Replacer` trait will work. For example,
55     /// a closure of type `|&Captures| -> String` provides direct access to the
56     /// captures corresponding to a match. This allows one to access
57     /// submatches easily:
58     ///
59     /// ```rust
60     /// # extern crate onig; use onig::Regex;
61     /// # use onig::Captures; fn main() {
62     /// let re = Regex::new(r"([^,\s]+),\s+(\S+)").unwrap();
63     /// let result = re.replace("Springsteen, Bruce", |caps: &Captures| {
64     ///     format!("{} {}", caps.at(2).unwrap_or(""), caps.at(1).unwrap_or(""))
65     /// });
66     /// assert_eq!(result, "Bruce Springsteen");
67     /// # }
68     /// ```
replace<R: Replacer>(&self, text: &str, rep: R) -> String69     pub fn replace<R: Replacer>(&self, text: &str, rep: R) -> String {
70         self.replacen(text, 1, rep)
71     }
72 
73     /// Replaces all non-overlapping matches in `text` with the
74     /// replacement provided. This is the same as calling `replacen` with
75     /// `limit` set to `0`.
76     ///
77     /// See the documentation for `replace` for details on how to access
78     /// submatches in the replacement string.
replace_all<R: Replacer>(&self, text: &str, rep: R) -> String79     pub fn replace_all<R: Replacer>(&self, text: &str, rep: R) -> String {
80         self.replacen(text, 0, rep)
81     }
82 
83     /// Replaces at most `limit` non-overlapping matches in `text` with the
84     /// replacement provided. If `limit` is 0, then all non-overlapping matches
85     /// are replaced.
86     ///
87     /// See the documentation for `replace` for details on how to access
88     /// submatches in the replacement string.
replacen<R: Replacer>(&self, text: &str, limit: usize, mut rep: R) -> String89     pub fn replacen<R: Replacer>(&self, text: &str, limit: usize, mut rep: R) -> String {
90         let mut new = String::with_capacity(text.len());
91         let mut last_match = 0;
92         for (i, cap) in self.captures_iter(text).enumerate() {
93             if limit > 0 && i >= limit {
94                 break;
95             }
96             // unwrap on 0 is OK because captures only reports matches
97             let (s, e) = cap.pos(0).unwrap();
98             new.push_str(&text[last_match..s]);
99             new.push_str(&rep.reg_replace(&cap));
100             last_match = e;
101         }
102         new.push_str(&text[last_match..]);
103         new
104     }
105 }
106