Skip to main content

ct_regex_internal/expr/
regex.rs

1use std::fmt::Debug;
2use std::ops::{ControlFlow, Range};
3
4use crate::anchor::Anchor;
5use crate::expr::{Capture, FindAllCaptures, FromRanges, IndexedCaptures, RangeOfAllMatches, SliceAllMatches};
6use crate::haystack::{
7    Haystack, HaystackItem, HaystackOf, HaystackSlice, IntoHaystack, OwnedHaystackable
8};
9use crate::matcher::Matcher;
10
11/// A trait that is automatically implemented for types produced by the `regex!` macro. Various
12/// function are included that test this pattern against a provided
13/// [`Haystack`](crate::haystack::Haystack).
14///
15/// Most methods will take an [`IntoHaystack`] or [`OwnedHaystackable`] parameter to save the
16/// user from creating their own `Haystack`. This allows values with types like `&str` and
17/// `&mut String` to be passed to these methods.
18///
19/// Although rarely encountered, this trait's generic parameter, `I` refers to the item that can be
20/// matched individually from the provided `Haystack`. This is used so that the same expression can
21/// be used to match various haystack types, including `&str` (`I = char`) and `&[u8]` (`I = u8`).
22/// Implementations for both of these slice/item pairs will be implemented by the macro.
23///
24/// # Function Coverage
25///
26#[doc = "| Description                            | Whole                            | First*                                       | All                                                                     |\n|-|-|-|-|\n| Checks for a match                     | [`is_match`](Self::is_match)     | [`contains_match`](Self::contains_match)     | [`count_matches`](Self::count_matches)                                  |\n| Returns range of match                 | -                                | [`range_of_match`](Self::range_of_match)     | [`range_of_all_matches`](Self::range_of_all_matches)                    |\n| Returns match as a slice               | -                                | [`slice_match`](Self::slice_match)           | [`slice_all_matches`](Self::slice_all_matches)                          |\n| Performs capturing using groups        | [`do_capture`](Self::do_capture) | [`find_capture`](Self::find_capture)         | [`find_all_captures`](Self::find_all_captures)                          |\n| Replaces match with value              | -                                | [`replace`](Self::replace)                   | [`replace_all`](Self::replace_all), [`_using`](Self::replace_all_using) |\n| Replaces match by transforming capture | -                                | [`replace_captured`](Self::replace_captured) | [`replace_all_captured`](Self::replace_all_captured)                    |"include_str!("coverage.md")]
27///
28/// [`Regex::replace_using_iter`] notably doesn't really fit in this table, because it only replaces
29/// while the `Iterator` yields values.
30///
31/// \* Note that these function runs through the Regex first and then the haystack. This means that
32/// substring involved is the one that matches the Regex first, not necessarily the first match in
33/// the haystack. In many cases, this makes no difference.
34pub trait Regex<I: HaystackItem, const N: usize>: Debug {
35    /// This type is a macro generated combination of ZSTs responsible for doing all of the heavy
36    /// lifting involved in actually matching or capturing against a `Haystack`. For realistic
37    /// expressions, this type will be very long an unpleasant to type, hence implementing it as an
38    /// associated type.
39    type Pattern: Matcher<I>;
40
41    /// This type is another macro generated combination of ZSTs representing the assertions that
42    /// the expression can make before attempting to match against a `Haystack`. This should be much
43    /// more minimal than [`Self::Pattern`] in most cases.
44    type Anchors: Anchor;
45
46    /// A macro generated type holding all `N` capture groups in this expression, producing ranges
47    /// or slices of the haystack with aliases for named groups. The generated type also understands
48    /// which groups will always exist in a match and which are optional.
49    ///
50    /// Note that an `Capture` type represents the contents of all groups from a single match. Each
51    /// group only occurs once, if you're expecting the expression the match multiple times in a
52    /// haystack, you will have many `Capture`s.
53    type Capture<'a, S: HaystackSlice<'a>>: Capture<'a, S> + FromRanges<'a, S, N> where I: 'a;
54
55    /// Returns `true` if this Regex matches the **entire** haystack provided. This should probably
56    /// be the default _matching_ function to use.
57    ///
58    /// A similar behavior can be achieved by using start and end anchors in an expression and then
59    /// calling [`contains_match`](Self::contains_match). This function should be preferred however,
60    /// because it fails fast if the first character doesn't match.
61    ///
62    /// To check if this Regex matches and perform capturing, use [`do_capture`](Self::do_capture)
63    /// instead.
64    fn is_match<'a, H: HaystackOf<'a, I>>(hay: impl IntoHaystack<'a, H>) -> bool {
65        let mut hay = hay.into_haystack();
66        if !Self::Anchors::assert_fixed(&hay) {
67            return false;
68        }
69
70        Self::Pattern::all_matches(&mut hay)
71            .any(|state| hay.rollback(state).is_end())
72    }
73
74    /// Returns `true` if this Regex matches any substring of the haystack provided. To retrieve the
75    /// actual substring itself, use [`slice_match`](Self::slice_match) or
76    /// [`find_capture`](Self::find_capture).
77    ///
78    /// Anchors can be used as a part of this Regex to perform more complex behaviors, but if you're
79    /// just wrapping an expression with `^` and `$`, see [`is_match`](Self::is_match) instead.
80    fn contains_match<'a, H: HaystackOf<'a, I>>(hay: impl IntoHaystack<'a, H>) -> bool {
81        let mut hay = hay.into_haystack();
82
83        loop {
84            let last_check = hay.item().is_none();
85            match Self::Anchors::assert(&hay) {
86                ControlFlow::Break(()) => {
87                    return false;
88                },
89                ControlFlow::Continue(true) => {
90                    let start = hay.index();
91
92                    if Self::Pattern::all_matches(&mut hay).next().is_some() {
93                        return true;
94                    }
95                    hay.rollback(start);
96                },
97                ControlFlow::Continue(false) => {}
98            }
99
100            if last_check {
101                return false;
102            }
103            hay.progress();
104        }
105    }
106
107    /// Returns the number of matches present in the haystack provided, optionally including
108    /// `overlapping` matches.
109    ///
110    /// If using this in conjunction with the actual matches themselves, you might be better of
111    /// collecting the other output and checking the length.
112    fn count_matches<'a, H: HaystackOf<'a, I>>(
113        hay: impl IntoHaystack<'a, H>,
114        overlapping: bool,
115    ) -> usize {
116        let mut hay = hay.into_haystack();
117        let mut count = 0;
118
119        loop {
120            let last_check = hay.item().is_none();
121            match Self::Anchors::assert(&hay) {
122                ControlFlow::Break(()) => {
123                    return count;
124                },
125                ControlFlow::Continue(true) => {
126                    let start = hay.index();
127
128                    if let Some(state_fork) = Self::Pattern::all_matches(&mut hay).next() {
129                        count += 1;
130
131                        // If start == state_fork, we have a zero-width pattern and have already
132                        // matched this index. We need to progress normally.
133
134                        if !overlapping && start != state_fork {
135                            hay.rollback(state_fork);
136
137                            if last_check {
138                                return count;
139                            }
140                            continue;
141                        }
142                    }
143                    hay.rollback(start);
144                },
145                ControlFlow::Continue(false) => {}
146            }
147
148            if last_check {
149                return count;
150            }
151            hay.progress();
152        }
153    }
154
155    /// Returns the range that matches this Regex first. This is the range variant of
156    /// [`contains_match`](Self::contains_match). For the actual substring itself, see
157    /// [`slice_match`](Self::slice_match).
158    ///
159    /// Note that there is no range equivalent of [`is_match`](Self::is_match), because any match
160    /// has to be the entire haystack.
161    fn range_of_match<'a, H: HaystackOf<'a, I>>(
162        hay: impl IntoHaystack<'a, H>,
163    ) -> Option<Range<usize>> {
164        let mut hay = hay.into_haystack();
165        range_of_match::<Self, _, _>(&mut hay)
166    }
167
168    /// Returns an iterator over the ranges of all substrings in the provided haystack that match
169    /// this Regex, optionally `overlapping`. For the actual substrings themselves, see
170    /// [`slice_all_matches`](Self::slice_all_matches).
171    ///
172    /// Note that each match is still made greedily. Even with `overlapping = true`, if two possible
173    /// matches start at the same index in the haystack, only the first to match the regex will be
174    /// included.
175    fn range_of_all_matches<'a, H: HaystackOf<'a, I>>(
176        hay: impl IntoHaystack<'a, H>,
177        overlapping: bool,
178    ) -> RangeOfAllMatches<'a, Self, I, H, N> {
179        RangeOfAllMatches::new(hay.into_haystack(), overlapping)
180    }
181
182    /// Returns the slice that matches this Regex first. This is the slicing variant of
183    /// [`range_of_match`](Self::range_of_match).
184    ///
185    /// Note that there is no slicing equivalent of [`is_match`](Self::is_match), because any match
186    /// has to be the entire haystack.
187    fn slice_match<'a, H: HaystackOf<'a, I>>(hay: impl IntoHaystack<'a, H>) -> Option<H::Slice> {
188        let mut hay = hay.into_haystack();
189        let range = range_of_match::<Self, _, _>(&mut hay)?;
190        Some(hay.slice_with(range))
191    }
192
193    /// Returns an iterator over all slices of the provided haystack that match this Regex,
194    /// optionally `overlapping`.
195    ///
196    /// Note that each match is still made greedily. Even with `overlapping = true`, if two possible
197    /// matches start at the same index in the haystack, only the first to match the regex will be
198    /// included.
199    ///
200    /// The returned iterator doesn't implement [`ExactSizeIterator`] because it is lazy, if you
201    /// need to know how many matches are included, [`collect`](Iterator::collect) it first.
202    fn slice_all_matches<'a, H: HaystackOf<'a, I>>(
203        hay: impl IntoHaystack<'a, H>,
204        overlapping: bool,
205    ) -> SliceAllMatches<'a, Self, I, H, N> {
206        SliceAllMatches {
207            inner: RangeOfAllMatches::new(hay.into_haystack(), overlapping),
208        }
209    }
210
211    /// Returns a [`Self::Capture`] representing the provided haystack matched against this Regex.
212    /// This includes any named or numbered capturing groups in the expression. As with
213    /// [`is_match`](Self::is_match), this function acts on the entire haystack, and needs to match
214    /// every character from start to end.
215    ///
216    /// Provides the same result as [`find_capture`](Self::find_capture) with start and end anchors,
217    /// although without needing to check any non-starting substring.
218    fn do_capture<'a, H: HaystackOf<'a, I>>(
219        hay: impl IntoHaystack<'a, H>,
220    ) -> Option<Self::Capture<'a, H::Slice>> {
221        let mut hay = hay.into_haystack();
222        if !Self::Anchors::assert_fixed(&hay) {
223            return None;
224        }
225
226        let mut caps = IndexedCaptures::default();
227        let start = hay.index();
228
229        let all_captures = Self::Pattern::all_captures(&mut hay, &mut caps);
230
231        for (state_fork, mut caps_fork) in all_captures {
232            if hay.rollback(state_fork).is_end() {
233                caps_fork.push(0, start..state_fork);
234
235                return Some(
236                    Self::Capture::from_ranges(caps_fork.into_array(), hay.inner_slice())
237                        .expect("failed to convert captures despite matching correctly")
238                );
239            }
240        }
241        None
242    }
243
244    /// Returns the [`Self::Capture`] that matches this Regex first, similar to
245    /// [`slice_match`](Self::slice_match) but with any named or numbered groups included.
246    ///
247    /// Anchors should be used for complex behavior, beyond unconditional start and end matches. See
248    /// [`do_capture`](Self::do_capture) instead to capture a full haystack.
249    fn find_capture<'a, H: HaystackOf<'a, I>>(
250        hay: impl IntoHaystack<'a, H>,
251    ) -> Option<Self::Capture<'a, H::Slice>> {
252        let mut hay = hay.into_haystack();
253
254        loop {
255            let last_check = hay.item().is_none();
256            if Self::Anchors::assert(&hay).continue_value()? {
257                let start = hay.index();
258                let mut caps = IndexedCaptures::default();
259
260                let first = Self::Pattern::all_captures(&mut hay, &mut caps).next();
261
262                if let Some((state_fork, mut caps_fork)) = first {
263                    caps_fork.push(0, start..state_fork);
264
265                    return Some(
266                        Self::Capture::from_ranges(caps_fork.into_array(), hay.inner_slice())
267                            .expect("failed to convert captures despite matching correctly")
268                    );
269                }
270                hay.rollback(start);
271            }
272
273            if last_check {
274                return None;
275            }
276            hay.progress()
277        }
278    }
279
280    /// Returns an iterator over [`Self::Capture`]s representing every full match of this Regex in
281    /// the provided haystack, similar to [`slice_all_matches`](Self::slice_all_matches). This can
282    /// optionally include `overlapping` matches.
283    ///
284    /// Note that each match is still made greedily. Even with `overlapping = true`, if two possible
285    /// matches start at the same index in the haystack, only the first to match the regex will be
286    /// included.
287    fn find_all_captures<'a, H: HaystackOf<'a, I>>(
288        hay: impl IntoHaystack<'a, H>,
289        overlapping: bool,
290    ) -> FindAllCaptures<'a, Self, I, H, N> {
291        FindAllCaptures::new(hay.into_haystack(), overlapping)
292    }
293
294    /// Replaces the first match of this Regex in the provided haystack with the provided slice. The
295    /// slice type required is the one associated with the provided haystack. The return value is a
296    /// boolean indicating whether a match was found and replaced.
297    fn replace<'a, M: OwnedHaystackable<I>>(
298        hay_mut: &mut M,
299        with: <M::Hay<'a> as Haystack<'a>>::Slice
300    ) -> bool {
301        let Some(range) = ({
302            let mut hay = hay_mut.as_haystack();
303            range_of_match::<Self, _, _>(&mut hay)
304        }) else {
305            return false;
306        };
307        hay_mut.replace_range(range, with);
308        true
309    }
310
311    /// Replaces every matching substring in the provided haystack with a copy of the provided
312    /// slice. The slice type required is the one associated with the provided haystack. The return
313    /// value is an integer representing the number of matches and replacements that occurred.
314    fn replace_all<'a, M: OwnedHaystackable<I>>(
315        hay_mut: &mut M,
316        with: <M::Hay<'a> as Haystack<'a>>::Slice
317    ) -> usize {
318        // Avoids redirecting to replace_all_using to avoid unnecessary clones.
319        let ranges = RangeOfAllMatches::<Self, I, M::Hay<'_>, N>::new(
320            hay_mut.as_haystack(),
321            false
322        ).collect::<Vec<_>>();
323
324        let count = ranges.len();
325        let mut delta = Delta::default();
326
327        for mut range in ranges {
328            delta.apply_to(&mut range);
329
330            let initial_len = hay_mut.len();
331            hay_mut.replace_range(range, with.clone());
332            delta.add_diff(hay_mut.len(), initial_len);
333        }
334
335        count
336    }
337
338    /// Replaces every matching substring in the provided haystack with the return value of the
339    /// provided function. The return type of this function needs to match the provided haystack.
340    /// The returned integer represents the number of matches and replacements that occurred.
341    ///
342    /// Because of the use of [`FnMut`] for the parameter, this can be used to replace all matches
343    /// using an iterator by passing in `|| iter.next().unwrap_or_default()`.
344    fn replace_all_using<M: OwnedHaystackable<I>>(
345        hay_mut: &mut M,
346        mut using: impl FnMut() -> M,
347    ) -> usize {
348        // Collect the Iterator to end the borrow of hay_mut.
349        let ranges = RangeOfAllMatches::<Self, I, M::Hay<'_>, N>::new(
350            hay_mut.as_haystack(),
351            false
352        ).collect::<Vec<_>>();
353
354        let count = ranges.len();
355        let mut delta = Delta::default();
356
357        for mut range in ranges {
358            delta.apply_to(&mut range);
359
360            let initial_len = hay_mut.len();
361            hay_mut.replace_range(range, using().as_slice());
362            delta.add_diff(hay_mut.len(), initial_len);
363        }
364
365        count
366    }
367
368    // TODO: Does this need to be Item = M, or can it be M::Slice?
369
370    /// Replaces matching substrings in the provided haystack with the values produced by the
371    /// iterator, until no matches remain or the iterator returns `None`. The return type of this
372    /// iterator needs to match the provided haystack. The returned integer represents the number of
373    /// matches and replacements that occurred.
374    ///
375    /// To maintain ownership of the iterator in the event that the haystack runs out of matches
376    /// before it is exhausted, use [`Iterator::by_ref`].
377    fn replace_using_iter<M: OwnedHaystackable<I>>(
378        hay_mut: &mut M,
379        iter: impl IntoIterator<Item = M>,
380    ) -> usize {
381        let iter = iter.into_iter();
382
383        // Zip with ranges first, because it is internal and the side effects don't matter.
384        let ranges_with_replacement = RangeOfAllMatches::<Self, I, M::Hay<'_>, N>::new(
385            hay_mut.as_haystack(),
386            false
387        ).zip(iter).collect::<Vec<_>>();
388
389        let mut count = 0;
390        let mut delta = Delta::default();
391
392        for (mut range, replacement) in ranges_with_replacement {
393            delta.apply_to(&mut range);
394
395            let initial_len = hay_mut.len();
396            hay_mut.replace_range(range, replacement.as_slice());
397            delta.add_diff(hay_mut.len(), initial_len);
398            count += 1;
399        }
400
401        count
402    }
403
404    // The closure returns M because it can't continue to reference the source, given that we need
405    // to overwrite it.
406
407    /// Replaces the first captured substring in the provided haystack with a computed value. The
408    /// return value is a boolean indicating whether a match was found and replaced.
409    ///
410    /// The provided function is used to create a replacement value when given the capture. The
411    /// replacement value shares a type with the provided haystack. Its simplified signature would
412    /// be `F: FnOnce(Self::Capture<'_, <M::Hay>::Slice>) -> M`. Because of limitations with higher
413    /// ranked trait bounds surrounding closure, it may be necessary to implement this as function
414    /// with lifetime annotations like so:
415    /// ```ignore
416    /// regex!(PhoneNum = r"(0|(?<country_code>\+[0-9]+))(?<number>[0-9]{9})");
417    ///
418    /// fn remove_country_code<'a>(value: PhoneNumCapture<'a, &'a str>) -> String {
419    ///     format!("0{}", value.number())
420    /// }
421    ///
422    /// fn main() {
423    ///     let mut hay = String::from("+1234567890");
424    ///     PhoneNum::replace_captured(hay, remove_country_code);
425    ///     assert_eq!(hay, "0234567890");
426    /// }
427    /// ```
428    fn replace_captured<M, F>(hay_mut: &mut M, replacer: F) -> bool
429    where
430        M: OwnedHaystackable<I>,
431        F: for<'a> FnOnce(Self::Capture<'a, <M::Hay<'a> as Haystack<'a>>::Slice>) -> M,
432    {
433        let (range, replacement) = {
434            let Some(caps) = Self::find_capture(hay_mut.as_haystack()) else {
435                return false;
436            };
437            let first = caps.whole_match_range().clone();
438
439            (first, replacer(caps))
440        };
441        hay_mut.replace_range(range, replacement.as_slice());
442        true
443    }
444
445    /// Replaces all captured substring in the provided haystack with a computed value. The return
446    /// value is an integer indicating the number of matches found and replaced.
447    ///
448    /// The provided function is used to create a replacement value when given a capture. The
449    /// replacement value shares a type with the provided haystack. Its simplified signature would
450    /// be `F: FnMut(Self::Capture<'_, <M::Hay>::Slice>) -> M`. Because of limitations with higher
451    /// ranked trait bounds surrounding closure, it may be necessary to implement this as function
452    /// with lifetime annotations as mentioned in the documentation for
453    /// [`replace_captured`](Self::replace_captured).
454    fn replace_all_captured<M, F>(hay_mut: &mut M, mut replacer: F) -> usize
455    where
456        M: OwnedHaystackable<I>,
457        F: for<'a> FnMut(Self::Capture<'a, <M::Hay<'a> as Haystack<'a>>::Slice>) -> M,
458    {
459        // Collect the Iterator to end the borrow of hay_mut.
460        let replacements: Vec<_> = {
461            let caps = Self::find_all_captures(hay_mut.as_haystack(), false);
462            caps.into_iter()
463                .map(|c| (c.whole_match_range().clone(), replacer(c)))
464                .collect()
465        };
466
467        let count = replacements.len();
468        let mut delta = Delta::default();
469
470        for (mut range, replacement) in replacements {
471            delta.apply_to(&mut range);
472
473            let initial_len = hay_mut.len();
474            hay_mut.replace_range(range, replacement.as_slice());
475            delta.add_diff(hay_mut.len(), initial_len);
476        }
477
478        count
479    }
480}
481
482fn range_of_match<'a, R: Regex<I, N> + ?Sized, I: HaystackItem, const N: usize>(
483    hay: &mut impl HaystackOf<'a, I>,
484) -> Option<Range<usize>> {
485    loop {
486        let last_check = hay.item().is_none();
487        if R::Anchors::assert(&*hay).continue_value()? {
488            let start = hay.index();
489
490            if let Some(state_fork) = R::Pattern::all_matches(hay).next() {
491                return Some(start..state_fork);
492            }
493            hay.rollback(start);
494        }
495        if last_check {
496            return None;
497        }
498        hay.progress()
499    }
500}
501
502/// A helper type for tracking changes in [`OwnedHaystackable`] size when replacing ranges. The type
503/// understands using signed addition for unsigned results.
504#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Delta {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Delta",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for Delta {
    #[inline]
    fn default() -> Delta { Delta(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::clone::Clone for Delta {
    #[inline]
    fn clone(&self) -> Delta { Delta(::core::clone::Clone::clone(&self.0)) }
}Clone)]
505struct Delta(isize);
506
507impl Delta {
508    /// Add to this `Delta` the difference between the new length, `from`, and the old length, `to`.
509    fn add_diff(&mut self, from: usize, to: usize) {
510        self.0 = self.0.strict_add(
511            from.checked_signed_diff(to)
512                .expect("difference between usizes doesn't fit in an isize")
513        )
514    }
515
516    /// Apply this `Delta` to both elements of `range`.
517    fn apply_to(&self, range: &mut Range<usize>) {
518        range.start = range.start.strict_add_signed(self.0);
519        range.end = range.end.strict_add_signed(self.0);
520    }
521}