From a1dfdd186049346e5b5dbc6c02cae432170edeb1 Mon Sep 17 00:00:00 2001
From: Andrew Gallant <jamslam@gmail.com>
Date: Mon, 6 Mar 2023 11:17:22 -0500
Subject: [PATCH] api: add Regex::captures_at

This isn't *strictly* needed because of the existence of
Regex::captures_read_at, but it does fill out the singular missing
method. Namely, all other search routines have an *_at variant, so we
might as well add it for Regex::captures too.

Closes #547
---
 src/re_bytes.rs   | 26 ++++++++++++++++++++------
 src/re_unicode.rs | 26 ++++++++++++++++++++------
 2 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/src/re_bytes.rs b/src/re_bytes.rs
index 7d488a95b..fcc9cd5bb 100644
--- a/src/re_bytes.rs
+++ b/src/re_bytes.rs
@@ -265,12 +265,7 @@ impl Regex {
     /// The `0`th capture group is always unnamed, so it must always be
     /// accessed with `get(0)` or `[0]`.
     pub fn captures<'t>(&self, text: &'t [u8]) -> Option<Captures<'t>> {
-        let mut locs = self.capture_locations();
-        self.captures_read_at(&mut locs, text, 0).map(move |_| Captures {
-            text,
-            locs: locs.0,
-            named_groups: self.0.capture_name_idx().clone(),
-        })
+        self.captures_at(text, 0)
     }
 
     /// Returns an iterator over all the non-overlapping capture groups matched
@@ -617,6 +612,25 @@ impl Regex {
             .map(|(s, e)| Match::new(text, s, e))
     }
 
+    /// Returns the same as [`Regex::captures`], but starts the search at the
+    /// given offset.
+    ///
+    /// The significance of the starting point is that it takes the surrounding
+    /// context into consideration. For example, the `\A` anchor can only
+    /// match when `start == 0`.
+    pub fn captures_at<'t>(
+        &self,
+        text: &'t [u8],
+        start: usize,
+    ) -> Option<Captures<'t>> {
+        let mut locs = self.capture_locations();
+        self.captures_read_at(&mut locs, text, start).map(move |_| Captures {
+            text,
+            locs: locs.0,
+            named_groups: self.0.capture_name_idx().clone(),
+        })
+    }
+
     /// This is like `captures`, but uses
     /// [`CaptureLocations`](struct.CaptureLocations.html)
     /// instead of
diff --git a/src/re_unicode.rs b/src/re_unicode.rs
index 1e8bd0453..296736080 100644
--- a/src/re_unicode.rs
+++ b/src/re_unicode.rs
@@ -321,12 +321,7 @@ impl Regex {
     /// The `0`th capture group is always unnamed, so it must always be
     /// accessed with `get(0)` or `[0]`.
     pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
-        let mut locs = self.capture_locations();
-        self.captures_read_at(&mut locs, text, 0).map(move |_| Captures {
-            text,
-            locs: locs.0,
-            named_groups: self.0.capture_name_idx().clone(),
-        })
+        self.captures_at(text, 0)
     }
 
     /// Returns an iterator over all the non-overlapping capture groups matched
@@ -675,6 +670,25 @@ impl Regex {
             .map(|(s, e)| Match::new(text, s, e))
     }
 
+    /// Returns the same as [`Regex::captures`], but starts the search at the
+    /// given offset.
+    ///
+    /// The significance of the starting point is that it takes the surrounding
+    /// context into consideration. For example, the `\A` anchor can only
+    /// match when `start == 0`.
+    pub fn captures_at<'t>(
+        &self,
+        text: &'t str,
+        start: usize,
+    ) -> Option<Captures<'t>> {
+        let mut locs = self.capture_locations();
+        self.captures_read_at(&mut locs, text, start).map(move |_| Captures {
+            text,
+            locs: locs.0,
+            named_groups: self.0.capture_name_idx().clone(),
+        })
+    }
+
     /// This is like `captures`, but uses
     /// [`CaptureLocations`](struct.CaptureLocations.html)
     /// instead of