Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix lifetimes for subcapture iterators in str regexes #214

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/re_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,21 +682,21 @@ impl<'t> Captures<'t> {

/// Creates an iterator of all the capture groups in order of appearance
/// in the regular expression.
pub fn iter<'a>(&'a self) -> SubCaptures<'a, 't> {
pub fn iter<'c>(&'c self) -> SubCaptures<'c, 't> {
SubCaptures { idx: 0, caps: self }
}

/// Creates an iterator of all the capture group positions in order of
/// appearance in the regular expression. Positions are byte indices
/// in terms of the original string matched.
pub fn iter_pos(&'t self) -> SubCapturesPos<'t> {
pub fn iter_pos<'c>(&'c self) -> SubCapturesPos<'c> {
SubCapturesPos { idx: 0, slots: &self.slots }
}

/// Creates an iterator of all named groups as an tuple with the group
/// name and the value. The iterator returns these values in arbitrary
/// order.
pub fn iter_named<'a>(&'a self) -> SubCapturesNamed<'a, 't> {
pub fn iter_named<'c>(&'c self) -> SubCapturesNamed<'c, 't> {
SubCapturesNamed {
caps: self,
names: self.named_groups.iter()
Expand Down
32 changes: 17 additions & 15 deletions src/re_unicode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,21 +874,21 @@ impl<'t> Captures<'t> {

/// Creates an iterator of all the capture groups in order of appearance
/// in the regular expression.
pub fn iter(&'t self) -> SubCaptures<'t> {
pub fn iter<'c>(&'c self) -> SubCaptures<'c, 't> {
SubCaptures { idx: 0, caps: self, }
}

/// Creates an iterator of all the capture group positions in order of
/// appearance in the regular expression. Positions are byte indices
/// in terms of the original string matched.
pub fn iter_pos(&'t self) -> SubCapturesPos<'t> {
pub fn iter_pos<'c>(&'c self) -> SubCapturesPos<'c> {
SubCapturesPos { idx: 0, slots: &self.slots }
}

/// Creates an iterator of all named groups as an tuple with the group
/// name and the value. The iterator returns these values in arbitrary
/// order.
pub fn iter_named(&'t self) -> SubCapturesNamed<'t> {
pub fn iter_named<'c>(&'c self) -> SubCapturesNamed<'c, 't> {
SubCapturesNamed {
caps: self,
names: self.named_groups.iter()
Expand Down Expand Up @@ -1013,16 +1013,17 @@ impl<'t, 'i> Index<&'i str> for Captures<'t> {
/// An iterator over capture groups for a particular match of a regular
/// expression.
///
/// `'c` is the lifetime of the captures.
pub struct SubCaptures<'c> {
/// `'c` is the lifetime of the captures and `'t` is the lifetime of the
/// matched text.
pub struct SubCaptures<'c, 't: 'c> {
idx: usize,
caps: &'c Captures<'c>,
caps: &'c Captures<'t>,
}

impl<'c> Iterator for SubCaptures<'c> {
type Item = Option<&'c str>;
impl<'c, 't> Iterator for SubCaptures<'c, 't> {
type Item = Option<&'t str>;

fn next(&mut self) -> Option<Option<&'c str>> {
fn next(&mut self) -> Option<Option<&'t str>> {
if self.idx < self.caps.len() {
self.idx += 1;
Some(self.caps.at(self.idx - 1))
Expand Down Expand Up @@ -1063,16 +1064,17 @@ impl<'c> Iterator for SubCapturesPos<'c> {
/// An Iterator over named capture groups as a tuple with the group
/// name and the value.
///
/// `'c` is the lifetime of the captures.
pub struct SubCapturesNamed<'c> {
caps: &'c Captures<'c>,
/// `'c` is the lifetime of the captures and `'t` is the lifetime of the
/// matched text.
pub struct SubCapturesNamed<'c, 't: 'c> {
caps: &'c Captures<'t>,
names: NamedGroupsIter<'c>,
}

impl<'c> Iterator for SubCapturesNamed<'c> {
type Item = (&'c str, Option<&'c str>);
impl<'c, 't> Iterator for SubCapturesNamed<'c, 't> {
type Item = (&'c str, Option<&'t str>);

fn next(&mut self) -> Option<(&'c str, Option<&'c str>)> {
fn next(&mut self) -> Option<(&'c str, Option<&'t str>)> {
self.names.next().map(|(name, pos)| (name, self.caps.at(pos)))
}
}
Expand Down
23 changes: 15 additions & 8 deletions tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,13 @@ fn capture_iter_missing() {
#[test]
fn capture_iter_pos() {
let re = regex!(r"(.)(?P<a>.)(.)(?P<b>.)");
let cap = re.captures(t!("abcd")).unwrap();

let expected = vec![
(0, 4), (0, 1), (1, 2), (2, 3), (3, 4),
].into_iter().map(Some).collect::<Vec<_>>();
let got = cap.iter_pos().collect::<Vec<_>>();
let got = {
let cap = re.captures(t!("abcd")).unwrap();
cap.iter_pos().collect::<Vec<_>>()
};
assert_eq!(expected, got);
}

Expand All @@ -192,15 +193,21 @@ fn capture_iter_pos_missing() {
#[test]
fn capture_iter_named() {
let re = regex!(r"(.)(?P<a>.)(.)(?P<b>.)");
let cap = re.captures(t!("abcd")).unwrap();

let expected1 = vec![
("a", Some(t!("b"))), ("b", Some(t!("d"))),
(String::from("a"), Some(t!("b"))), (String::from("b"), Some(t!("d"))),
];
let expected2 = vec![
("b", Some(t!("d"))), ("a", Some(t!("b"))),
(String::from("b"), Some(t!("d"))), (String::from("a"), Some(t!("b"))),
];
let got = cap.iter_named().collect::<Vec<_>>();
let got = {
let cap = re.captures(t!("abcd")).unwrap();
// The names live as long as the captures object, so we clone them.
// The texts, on the other hand, must have the lifetime of the matched
// text (in this case, 'static).
cap.iter_named()
.map(|(name, text)| (name.to_owned(), text))
.collect::<Vec<_>>()
};
assert!(got == expected1 || got == expected2);
}

Expand Down