fancy_regex/
replacer.rs

1use alloc::borrow::Cow;
2use alloc::string::String;
3
4use crate::Captures;
5
6/// Replacer describes types that can be used to replace matches in a string.
7///
8/// In general, users of this crate shouldn't need to implement this trait,
9/// since implementations are already provided for `&str` along with other
10/// variants of string types and `FnMut(&Captures) -> String` (or any
11/// `FnMut(&Captures) -> T` where `T: AsRef<str>`), which covers most use cases.
12pub trait Replacer {
13    /// Appends text to `dst` to replace the current match.
14    ///
15    /// The current match is represented by `caps`, which is guaranteed to
16    /// have a match at capture group `0`.
17    ///
18    /// For example, a no-op replacement would be
19    /// `dst.push_str(caps.get(0).unwrap().as_str())`.
20    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String);
21
22    /// Return a fixed unchanging replacement string.
23    ///
24    /// When doing replacements, if access to `Captures` is not needed (e.g.,
25    /// the replacement byte string does not need `$` expansion), then it can
26    /// be beneficial to avoid finding sub-captures.
27    ///
28    /// In general, this is called once for every call to `replacen`.
29    fn no_expansion(&mut self) -> Option<Cow<str>> {
30        None
31    }
32
33    /// Return a `Replacer` that borrows and wraps this `Replacer`.
34    ///
35    /// This is useful when you want to take a generic `Replacer` (which might
36    /// not be cloneable) and use it without consuming it, so it can be used
37    /// more than once.
38    ///
39    /// # Example
40    ///
41    /// ```
42    /// use fancy_regex::{Regex, Replacer};
43    ///
44    /// fn replace_all_twice<R: Replacer>(
45    ///     re: Regex,
46    ///     src: &str,
47    ///     mut rep: R,
48    /// ) -> String {
49    ///     let dst = re.replace_all(src, rep.by_ref());
50    ///     let dst = re.replace_all(&dst, rep.by_ref());
51    ///     dst.into_owned()
52    /// }
53    /// ```
54    fn by_ref(&mut self) -> ReplacerRef<Self> {
55        ReplacerRef(self)
56    }
57}
58
59/// By-reference adaptor for a `Replacer`
60///
61/// Returned by [`Replacer::by_ref`](trait.Replacer.html#method.by_ref).
62#[derive(Debug)]
63pub struct ReplacerRef<'a, R: ?Sized>(&'a mut R);
64
65impl<'a, R: Replacer + ?Sized + 'a> Replacer for ReplacerRef<'a, R> {
66    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
67        self.0.replace_append(caps, dst)
68    }
69    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
70        self.0.no_expansion()
71    }
72}
73
74impl<'a> Replacer for &'a str {
75    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
76        caps.expand(*self, dst);
77    }
78
79    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
80        no_expansion(self)
81    }
82}
83
84impl<'a> Replacer for &'a String {
85    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
86        self.as_str().replace_append(caps, dst)
87    }
88
89    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
90        no_expansion(self)
91    }
92}
93
94impl Replacer for String {
95    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
96        self.as_str().replace_append(caps, dst)
97    }
98
99    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
100        no_expansion(self)
101    }
102}
103
104impl<'a> Replacer for Cow<'a, str> {
105    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
106        self.as_ref().replace_append(caps, dst)
107    }
108
109    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
110        no_expansion(self)
111    }
112}
113
114impl<'a> Replacer for &'a Cow<'a, str> {
115    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
116        self.as_ref().replace_append(caps, dst)
117    }
118
119    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
120        no_expansion(self)
121    }
122}
123
124fn no_expansion<T: AsRef<str>>(t: &T) -> Option<Cow<'_, str>> {
125    let s = t.as_ref();
126    if s.contains('$') {
127        None
128    } else {
129        Some(Cow::Borrowed(s))
130    }
131}
132
133impl<F, T> Replacer for F
134where
135    F: FnMut(&Captures<'_>) -> T,
136    T: AsRef<str>,
137{
138    fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
139        dst.push_str((*self)(caps).as_ref());
140    }
141}
142
143/// `NoExpand` indicates literal string replacement.
144///
145/// It can be used with `replace` and `replace_all` to do a literal string
146/// replacement without expanding `$name` to their corresponding capture
147/// groups. This can be both convenient (to avoid escaping `$`, for example)
148/// and performant (since capture groups don't need to be found).
149///
150/// `'t` is the lifetime of the literal text.
151#[derive(Clone, Debug)]
152pub struct NoExpand<'t>(pub &'t str);
153
154impl<'t> Replacer for NoExpand<'t> {
155    fn replace_append(&mut self, _: &Captures<'_>, dst: &mut String) {
156        dst.push_str(self.0);
157    }
158
159    fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
160        Some(Cow::Borrowed(self.0))
161    }
162}