ct_regex_internal/expr/regex.rs
1use std::fmt::Debug;
2use std::ops::{ControlFlow, Range};
3
4use crate::anchor::Anchor;
5use crate::expr::{
6 Capture, FindAllCaptures, FromRanges, IndexedCaptures, RangeOfAllMatches, SliceAllMatches,
7};
8use crate::haystack::{
9 Haystack, HaystackItem, HaystackOf, HaystackSlice, IntoHaystack, OwnedHaystackable,
10};
11use crate::matcher::Matcher;
12
13/// A trait that is automatically implemented for types produced by the `regex!` macro. Various
14/// function are included that test this pattern against a provided
15/// [`Haystack`](crate::haystack::Haystack).
16///
17/// Most methods will take an [`IntoHaystack`] or [`OwnedHaystackable`] parameter to save the
18/// user from creating their own `Haystack`. This allows values with types like `&str` and
19/// `&mut String` to be passed to these methods.
20///
21/// Although rarely encountered, this trait's generic parameter, `I` refers to the item that can be
22/// matched individually from the provided `Haystack`. This is used so that the same expression can
23/// be used to match various haystack types, including `&str` (`I = char`) and `&[u8]` (`I = u8`).
24/// Implementations for both of these slice/item pairs will be implemented by the macro.
25///
26/// # Function Coverage
27///
28#[doc = include_str!("coverage.md")]
29///
30/// [`Regex::replace_using_iter`] notably doesn't really fit in this table, because it only replaces
31/// while the `Iterator` yields values.
32///
33/// \* Note that these function runs through the Regex first and then the haystack. This means that
34/// substring involved is the one that matches the Regex first, not necessarily the first match in
35/// the haystack. In many cases, this makes no difference.
36pub trait Regex<I: HaystackItem, const N: usize>: Debug {
37 /// This type is a macro generated combination of ZSTs responsible for doing all of the heavy
38 /// lifting involved in actually matching or capturing against a `Haystack`. For realistic
39 /// expressions, this type will be very long an unpleasant to type, hence implementing it as an
40 /// associated type.
41 type Pattern: Matcher<I>;
42
43 /// This type is another macro generated combination of ZSTs representing the assertions that
44 /// the expression can make before attempting to match against a `Haystack`. This should be much
45 /// more minimal than [`Self::Pattern`] in most cases.
46 type Anchors: Anchor;
47
48 /// A macro generated type holding all `N` capture groups in this expression, producing ranges
49 /// or slices of the haystack with aliases for named groups. The generated type also understands
50 /// which groups will always exist in a match and which are optional.
51 ///
52 /// Note that an `Capture` type represents the contents of all groups from a single match. Each
53 /// group only occurs once, if you're expecting the expression the match multiple times in a
54 /// haystack, you will have many `Capture`s.
55 type Capture<'a, S: HaystackSlice<'a>>: Capture<'a, S> + FromRanges<'a, S, N> where I: 'a;
56
57 /// Returns `true` if this Regex matches the **entire** haystack provided. This should probably
58 /// be the default _matching_ function to use.
59 ///
60 /// A similar behavior can be achieved by using start and end anchors in an expression and then
61 /// calling [`contains_match`](Self::contains_match). This function should be preferred however,
62 /// because it fails fast if the first character doesn't match.
63 ///
64 /// To check if this Regex matches and perform capturing, use [`do_capture`](Self::do_capture)
65 /// instead.
66 fn is_match<'a, H: HaystackOf<'a, I>>(hay: impl IntoHaystack<'a, H>) -> bool {
67 let mut hay = hay.into_haystack();
68 if !Self::Anchors::assert_fixed(&hay) {
69 return false;
70 }
71
72 Self::Pattern::all_matches(&mut hay)
73 .any(|state| hay.rollback(state).is_end())
74 }
75
76 /// Returns `true` if this Regex matches any substring of the haystack provided. To retrieve the
77 /// actual substring itself, use [`slice_match`](Self::slice_match) or
78 /// [`find_capture`](Self::find_capture).
79 ///
80 /// Anchors can be used as a part of this Regex to perform more complex behaviors, but if you're
81 /// just wrapping an expression with `^` and `$`, see [`is_match`](Self::is_match) instead.
82 fn contains_match<'a, H: HaystackOf<'a, I>>(hay: impl IntoHaystack<'a, H>) -> bool {
83 let mut hay = hay.into_haystack();
84
85 loop {
86 let last_check = hay.item().is_none();
87 match Self::Anchors::assert(&hay) {
88 ControlFlow::Break(()) => {
89 return false;
90 },
91 ControlFlow::Continue(true) => {
92 let start = hay.index();
93
94 if Self::Pattern::all_matches(&mut hay).next().is_some() {
95 return true;
96 }
97 hay.rollback(start);
98 },
99 ControlFlow::Continue(false) => {},
100 }
101
102 if last_check {
103 return false;
104 }
105 hay.progress();
106 }
107 }
108
109 /// Returns the number of matches present in the haystack provided, optionally including
110 /// `overlapping` matches.
111 ///
112 /// If using this in conjunction with the actual matches themselves, you might be better of
113 /// collecting the other output and checking the length.
114 fn count_matches<'a, H: HaystackOf<'a, I>>(
115 hay: impl IntoHaystack<'a, H>,
116 overlapping: bool,
117 ) -> usize {
118 let mut hay = hay.into_haystack();
119 let mut count = 0;
120
121 loop {
122 let last_check = hay.item().is_none();
123 match Self::Anchors::assert(&hay) {
124 ControlFlow::Break(()) => {
125 return count;
126 },
127 ControlFlow::Continue(true) => {
128 let start = hay.index();
129
130 if let Some(state_fork) = Self::Pattern::all_matches(&mut hay).next() {
131 count += 1;
132
133 // If start == state_fork, we have a zero-width pattern and have already
134 // matched this index. We need to progress normally.
135
136 if !overlapping && start != state_fork {
137 hay.rollback(state_fork);
138
139 if last_check {
140 return count;
141 }
142 continue;
143 }
144 }
145 hay.rollback(start);
146 },
147 ControlFlow::Continue(false) => {},
148 }
149
150 if last_check {
151 return count;
152 }
153 hay.progress();
154 }
155 }
156
157 /// Returns the range that matches this Regex first. This is the range variant of
158 /// [`contains_match`](Self::contains_match). For the actual substring itself, see
159 /// [`slice_match`](Self::slice_match).
160 ///
161 /// Note that there is no range equivalent of [`is_match`](Self::is_match), because any match
162 /// has to be the entire haystack.
163 fn range_of_match<'a, H: HaystackOf<'a, I>>(
164 hay: impl IntoHaystack<'a, H>,
165 ) -> Option<Range<usize>> {
166 let mut hay = hay.into_haystack();
167 range_of_match::<Self, _, _>(&mut hay)
168 }
169
170 /// Returns an iterator over the ranges of all substrings in the provided haystack that match
171 /// this Regex, optionally `overlapping`. For the actual substrings themselves, see
172 /// [`slice_all_matches`](Self::slice_all_matches).
173 ///
174 /// Note that each match is still made greedily. Even with `overlapping = true`, if two possible
175 /// matches start at the same index in the haystack, only the first to match the regex will be
176 /// included.
177 fn range_of_all_matches<'a, H: HaystackOf<'a, I>>(
178 hay: impl IntoHaystack<'a, H>,
179 overlapping: bool,
180 ) -> RangeOfAllMatches<'a, Self, I, H, N> {
181 RangeOfAllMatches::new(hay.into_haystack(), overlapping)
182 }
183
184 /// Returns the slice that matches this Regex first. This is the slicing variant of
185 /// [`range_of_match`](Self::range_of_match).
186 ///
187 /// Note that there is no slicing equivalent of [`is_match`](Self::is_match), because any match
188 /// has to be the entire haystack.
189 fn slice_match<'a, H: HaystackOf<'a, I>>(hay: impl IntoHaystack<'a, H>) -> Option<H::Slice> {
190 let mut hay = hay.into_haystack();
191 let range = range_of_match::<Self, _, _>(&mut hay)?;
192 Some(hay.slice_with(range))
193 }
194
195 /// Returns an iterator over all slices of the provided haystack that match this Regex,
196 /// optionally `overlapping`.
197 ///
198 /// Note that each match is still made greedily. Even with `overlapping = true`, if two possible
199 /// matches start at the same index in the haystack, only the first to match the regex will be
200 /// included.
201 ///
202 /// The returned iterator doesn't implement [`ExactSizeIterator`] because it is lazy, if you
203 /// need to know how many matches are included, [`collect`](Iterator::collect) it first.
204 fn slice_all_matches<'a, H: HaystackOf<'a, I>>(
205 hay: impl IntoHaystack<'a, H>,
206 overlapping: bool,
207 ) -> SliceAllMatches<'a, Self, I, H, N> {
208 SliceAllMatches {
209 inner: RangeOfAllMatches::new(hay.into_haystack(), overlapping),
210 }
211 }
212
213 /// Returns a [`Self::Capture`] representing the provided haystack matched against this Regex.
214 /// This includes any named or numbered capturing groups in the expression. As with
215 /// [`is_match`](Self::is_match), this function acts on the entire haystack, and needs to match
216 /// every character from start to end.
217 ///
218 /// Provides the same result as [`find_capture`](Self::find_capture) with start and end anchors,
219 /// although without needing to check any non-starting substring.
220 fn do_capture<'a, H: HaystackOf<'a, I>>(
221 hay: impl IntoHaystack<'a, H>,
222 ) -> Option<Self::Capture<'a, H::Slice>> {
223 let mut hay = hay.into_haystack();
224 if !Self::Anchors::assert_fixed(&hay) {
225 return None;
226 }
227
228 let mut caps = IndexedCaptures::default();
229 let start = hay.index();
230
231 let all_captures = Self::Pattern::all_captures(&mut hay, &mut caps);
232
233 for (state_fork, mut caps_fork) in all_captures {
234 if hay.rollback(state_fork).is_end() {
235 caps_fork.push(0, start..state_fork);
236
237 return Some(
238 Self::Capture::from_ranges(caps_fork.into_array(), hay.inner_slice())
239 .expect("failed to convert captures despite matching correctly")
240 );
241 }
242 }
243 None
244 }
245
246 /// Returns the [`Self::Capture`] that matches this Regex first, similar to
247 /// [`slice_match`](Self::slice_match) but with any named or numbered groups included.
248 ///
249 /// Anchors should be used for complex behavior, beyond unconditional start and end matches. See
250 /// [`do_capture`](Self::do_capture) instead to capture a full haystack.
251 fn find_capture<'a, H: HaystackOf<'a, I>>(
252 hay: impl IntoHaystack<'a, H>,
253 ) -> Option<Self::Capture<'a, H::Slice>> {
254 let mut hay = hay.into_haystack();
255
256 loop {
257 let last_check = hay.item().is_none();
258 if Self::Anchors::assert(&hay).continue_value()? {
259 let start = hay.index();
260 let mut caps = IndexedCaptures::default();
261
262 let first = Self::Pattern::all_captures(&mut hay, &mut caps).next();
263
264 if let Some((state_fork, mut caps_fork)) = first {
265 caps_fork.push(0, start..state_fork);
266
267 return Some(
268 Self::Capture::from_ranges(caps_fork.into_array(), hay.inner_slice())
269 .expect("failed to convert captures despite matching correctly")
270 );
271 }
272 hay.rollback(start);
273 }
274
275 if last_check {
276 return None;
277 }
278 hay.progress()
279 }
280 }
281
282 /// Returns an iterator over [`Self::Capture`]s representing every full match of this Regex in
283 /// the provided haystack, similar to [`slice_all_matches`](Self::slice_all_matches). This can
284 /// optionally include `overlapping` matches.
285 ///
286 /// Note that each match is still made greedily. Even with `overlapping = true`, if two possible
287 /// matches start at the same index in the haystack, only the first to match the regex will be
288 /// included.
289 fn find_all_captures<'a, H: HaystackOf<'a, I>>(
290 hay: impl IntoHaystack<'a, H>,
291 overlapping: bool,
292 ) -> FindAllCaptures<'a, Self, I, H, N> {
293 FindAllCaptures::new(hay.into_haystack(), overlapping)
294 }
295
296 /// Replaces the first match of this Regex in the provided haystack with the provided slice. The
297 /// slice type required is the one associated with the provided haystack. The return value is a
298 /// boolean indicating whether a match was found and replaced.
299 fn replace<'a, M: OwnedHaystackable<I>>(
300 hay_mut: &mut M,
301 with: <M::Hay<'a> as Haystack<'a>>::Slice,
302 ) -> bool {
303 let Some(range) = ({
304 let mut hay = hay_mut.as_haystack();
305 range_of_match::<Self, _, _>(&mut hay)
306 }) else {
307 return false;
308 };
309 hay_mut.replace_range(range, with);
310 true
311 }
312
313 /// Replaces every matching substring in the provided haystack with a copy of the provided
314 /// slice. The slice type required is the one associated with the provided haystack. The return
315 /// value is an integer representing the number of matches and replacements that occurred.
316 fn replace_all<'a, M: OwnedHaystackable<I>>(
317 hay_mut: &mut M,
318 with: <M::Hay<'a> as Haystack<'a>>::Slice,
319 ) -> usize {
320 // Avoids redirecting to replace_all_using to avoid unnecessary clones.
321 let ranges = RangeOfAllMatches::<Self, I, M::Hay<'_>, N>::new(
322 hay_mut.as_haystack(),
323 false
324 ).collect::<Vec<_>>();
325
326 let count = ranges.len();
327 let mut delta = Delta::default();
328
329 for mut range in ranges {
330 delta.apply_to(&mut range);
331
332 let initial_len = hay_mut.len();
333 hay_mut.replace_range(range, with.clone());
334 delta.add_diff(hay_mut.len(), initial_len);
335 }
336
337 count
338 }
339
340 /// Replaces every matching substring in the provided haystack with the return value of the
341 /// provided function. The return type of this function needs to match the provided haystack.
342 /// The returned integer represents the number of matches and replacements that occurred.
343 ///
344 /// Because of the use of [`FnMut`] for the parameter, this can be used to replace all matches
345 /// using an iterator by passing in `|| iter.next().unwrap_or_default()`.
346 fn replace_all_using<M: OwnedHaystackable<I>>(
347 hay_mut: &mut M,
348 mut using: impl FnMut() -> M,
349 ) -> usize {
350 // Collect the Iterator to end the borrow of hay_mut.
351 let ranges = RangeOfAllMatches::<Self, I, M::Hay<'_>, N>::new(
352 hay_mut.as_haystack(),
353 false
354 ).collect::<Vec<_>>();
355
356 let count = ranges.len();
357 let mut delta = Delta::default();
358
359 for mut range in ranges {
360 delta.apply_to(&mut range);
361
362 let initial_len = hay_mut.len();
363 hay_mut.replace_range(range, using().as_slice());
364 delta.add_diff(hay_mut.len(), initial_len);
365 }
366
367 count
368 }
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(Debug, Default, 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}