Skip to content

Commit

Permalink
fixup! More generic impl of Replacer for closures
Browse files Browse the repository at this point in the history
Hide `ReplacerClosure` trait as an implementation detail and describe
for which closures the `Replacer` trait is implemented in the
documentation instead.

Added documentation tests.
  • Loading branch information
JanBeh committed Jul 20, 2023
1 parent adf66a8 commit a40d8f5
Showing 1 changed file with 83 additions and 16 deletions.
99 changes: 83 additions & 16 deletions src/regex/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2371,23 +2371,27 @@ impl<'c, 'h> ExactSizeIterator for SubCaptureMatches<'c, 'h> {}

impl<'c, 'h> core::iter::FusedIterator for SubCaptureMatches<'c, 'h> {}

/// If a closure implements this for all `'a`, then it also implements
/// [`Replacer`].
pub trait ReplacerClosure<'a>
where
Self: FnMut(&'a Captures<'_>) -> <Self as ReplacerClosure<'a>>::Output,
{
/// Return type of the closure (may depend on lifetime `'a`).
type Output: AsRef<str>;
}

impl<'a, F: ?Sized, O> ReplacerClosure<'a> for F
where
F: FnMut(&'a Captures<'_>) -> O,
O: AsRef<str>,
{
type Output = O;
/// Contains helper trait for blanket implementation for [`Replacer`].
mod replacer_closure {
use super::*;
/// If a closure implements this for all `'a` and `'b`, then it also
/// implements [`Replacer`].
pub trait ReplacerClosure<'a>
where
Self: FnMut(&'a Captures<'_>) -> <Self as ReplacerClosure<'a>>::Output,
{
/// Return type of the closure (may depend on lifetime `'a`).
type Output: AsRef<str>;
}
impl<'a, F: ?Sized, O> ReplacerClosure<'a> for F
where
F: FnMut(&'a Captures<'_>) -> O,
O: AsRef<str>,
{
type Output = O;
}
}
use replacer_closure::*;

/// A trait for types that can be used to replace matches in a haystack.
///
Expand Down Expand Up @@ -2421,6 +2425,69 @@ where
/// let result = re.replace("Springsteen, Bruce", NameSwapper);
/// assert_eq!(result, "Bruce Springsteen");
/// ```
///
/// # Implementation by closures
///
/// Closures that take an argument of type `&'a Captures<'b>` for any `'a` and
/// `'b: 'a` and which return a type `T: AsRef<str>` (that may depend on `'a`)
/// implement the `Replacer` trait through a blanket implementation.
///
/// A simple example looks like this:
///
/// ```
/// use regex::{Captures, Regex};
///
/// let re = Regex::new(r"[0-9]+").unwrap();
/// let result = re.replace_all("1234,12345", |caps: &Captures<'_>| {
/// format!("[number with {} digits]", caps[0].len())
/// });
/// assert_eq!(result, "[number with 4 digits],[number with 5 digits]");
/// ```
///
/// Note that the return type of the closure may depend on the lifetime of the
/// reference that is passed as an argument to the closure. This requires the
/// closure to be a function, unless [closure lifetime binders] are being used:
///
/// [closure lifetime binders]: https://rust-lang.github.io/rfcs/3216-closure-lifetime-binder.html
/// [`Cow`]: std::borrow::Cow
///
/// ```
/// use regex::{Captures, Regex, Replacer};
/// use std::borrow::Cow;
///
/// let re = Regex::new(r"[0-9]+").unwrap();
/// fn prepend_odd<'a>(caps: &'a Captures<'_>) -> Cow<'a, str> {
/// if caps[0].len() % 2 == 1 {
/// Cow::Owned(format!("0{}", &caps[0]))
/// } else {
/// Cow::Borrowed(&caps[0])
/// }
/// }
/// let result = re.replace_all("1234,12345", prepend_odd);
/// assert_eq!(result, "1234,012345");
/// ```
///
/// The same example using closure lifetime binders:
///
/// ```
/// #![feature(closure_lifetime_binder)]
///
/// use regex::{Captures, Regex, Replacer};
/// use std::borrow::Cow;
///
/// let re = Regex::new(r"[0-9]+").unwrap();
/// let result = re.replace_all(
/// "1234,12345",
/// for<'a, 'b> |caps: &'a Captures<'b>| -> Cow<'a, str> {
/// if caps[0].len() % 2 == 1 {
/// Cow::Owned(format!("0{}", &caps[0]))
/// } else {
/// Cow::Borrowed(&caps[0])
/// }
/// },
/// );
/// assert_eq!(result, "1234,012345");
/// ```
pub trait Replacer {
/// Appends possibly empty data to `dst` to replace the current match.
///
Expand Down

0 comments on commit a40d8f5

Please sign in to comment.