Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.5.6/src/re_trait.rs
Line
Count
Source
1
use std::fmt;
2
use std::iter::FusedIterator;
3
4
/// Slot is a single saved capture location. Note that there are two slots for
5
/// every capture in a regular expression (one slot each for the start and end
6
/// of the capture).
7
pub type Slot = Option<usize>;
8
9
/// Locations represents the offsets of each capturing group in a regex for
10
/// a single match.
11
///
12
/// Unlike `Captures`, a `Locations` value only stores offsets.
13
#[doc(hidden)]
14
#[derive(Clone, Debug)]
15
pub struct Locations(Vec<Slot>);
16
17
impl Locations {
18
    /// Returns the start and end positions of the Nth capture group. Returns
19
    /// `None` if `i` is not a valid capture group or if the capture group did
20
    /// not match anything. The positions returned are *always* byte indices
21
    /// with respect to the original string matched.
22
65.0k
    pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
23
65.0k
        let (s, e) = (i * 2, i * 2 + 1);
24
65.0k
        match (self.0.get(s), self.0.get(e)) {
25
40.4k
            (Some(&Some(s)), Some(&Some(e))) => Some((s, e)),
26
24.6k
            _ => None,
27
        }
28
65.0k
    }
<regex::re_trait::Locations>::pos
Line
Count
Source
22
8.78k
    pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
23
8.78k
        let (s, e) = (i * 2, i * 2 + 1);
24
8.78k
        match (self.0.get(s), self.0.get(e)) {
25
5.32k
            (Some(&Some(s)), Some(&Some(e))) => Some((s, e)),
26
3.45k
            _ => None,
27
        }
28
8.78k
    }
<regex::re_trait::Locations>::pos
Line
Count
Source
22
56.3k
    pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
23
56.3k
        let (s, e) = (i * 2, i * 2 + 1);
24
56.3k
        match (self.0.get(s), self.0.get(e)) {
25
35.0k
            (Some(&Some(s)), Some(&Some(e))) => Some((s, e)),
26
21.2k
            _ => None,
27
        }
28
56.3k
    }
29
30
    /// Creates an iterator of all the capture group positions in order of
31
    /// appearance in the regular expression. Positions are byte indices
32
    /// in terms of the original string matched.
33
0
    pub fn iter(&self) -> SubCapturesPosIter<'_> {
34
0
        SubCapturesPosIter { idx: 0, locs: self }
35
0
    }
Unexecuted instantiation: <regex::re_trait::Locations>::iter
Unexecuted instantiation: <regex::re_trait::Locations>::iter
36
37
    /// Returns the total number of capturing groups.
38
    ///
39
    /// This is always at least `1` since every regex has at least `1`
40
    /// capturing group that corresponds to the entire match.
41
0
    pub fn len(&self) -> usize {
42
0
        self.0.len() / 2
43
0
    }
Unexecuted instantiation: <regex::re_trait::Locations>::len
Unexecuted instantiation: <regex::re_trait::Locations>::len
44
45
    /// Return the individual slots as a slice.
46
66.6k
    pub(crate) fn as_slots(&mut self) -> &mut [Slot] {
47
66.6k
        &mut self.0
48
66.6k
    }
<regex::re_trait::Locations>::as_slots
Line
Count
Source
46
9.11k
    pub(crate) fn as_slots(&mut self) -> &mut [Slot] {
47
9.11k
        &mut self.0
48
9.11k
    }
<regex::re_trait::Locations>::as_slots
Line
Count
Source
46
57.5k
    pub(crate) fn as_slots(&mut self) -> &mut [Slot] {
47
57.5k
        &mut self.0
48
57.5k
    }
49
}
50
51
/// An iterator over capture group positions for a particular match of a
52
/// regular expression.
53
///
54
/// Positions are byte indices in terms of the original string matched.
55
///
56
/// `'c` is the lifetime of the captures.
57
#[derive(Clone, Debug)]
58
pub struct SubCapturesPosIter<'c> {
59
    idx: usize,
60
    locs: &'c Locations,
61
}
62
63
impl<'c> Iterator for SubCapturesPosIter<'c> {
64
    type Item = Option<(usize, usize)>;
65
66
0
    fn next(&mut self) -> Option<Option<(usize, usize)>> {
67
0
        if self.idx >= self.locs.len() {
68
0
            return None;
69
0
        }
70
0
        let x = match self.locs.pos(self.idx) {
71
0
            None => Some(None),
72
0
            Some((s, e)) => Some(Some((s, e))),
73
        };
74
0
        self.idx += 1;
75
0
        x
76
0
    }
Unexecuted instantiation: <regex::re_trait::SubCapturesPosIter as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::SubCapturesPosIter as core::iter::traits::iterator::Iterator>::next
77
}
78
79
impl<'c> FusedIterator for SubCapturesPosIter<'c> {}
80
81
/// `RegularExpression` describes types that can implement regex searching.
82
///
83
/// This trait is my attempt at reducing code duplication and to standardize
84
/// the internal API. Specific duplication that is avoided are the `find`
85
/// and `capture` iterators, which are slightly tricky.
86
///
87
/// It's not clear whether this trait is worth it, and it also isn't
88
/// clear whether it's useful as a public trait or not. Methods like
89
/// `next_after_empty` reak of bad design, but the rest of the methods seem
90
/// somewhat reasonable. One particular thing this trait would expose would be
91
/// the ability to start the search of a regex anywhere in a haystack, which
92
/// isn't possible in the current public API.
93
pub trait RegularExpression: Sized + fmt::Debug {
94
    /// The type of the haystack.
95
    type Text: ?Sized + fmt::Debug;
96
97
    /// The number of capture slots in the compiled regular expression. This is
98
    /// always two times the number of capture groups (two slots per group).
99
    fn slots_len(&self) -> usize;
100
101
    /// Allocates fresh space for all capturing groups in this regex.
102
66.6k
    fn locations(&self) -> Locations {
103
66.6k
        Locations(vec![None; self.slots_len()])
104
66.6k
    }
Unexecuted instantiation: <regex::exec::ExecNoSync as regex::re_trait::RegularExpression>::locations
<regex::exec::ExecNoSyncStr as regex::re_trait::RegularExpression>::locations
Line
Count
Source
102
9.11k
    fn locations(&self) -> Locations {
103
9.11k
        Locations(vec![None; self.slots_len()])
104
9.11k
    }
Unexecuted instantiation: <regex::exec::ExecNoSync as regex::re_trait::RegularExpression>::locations
<regex::exec::ExecNoSyncStr as regex::re_trait::RegularExpression>::locations
Line
Count
Source
102
57.5k
    fn locations(&self) -> Locations {
103
57.5k
        Locations(vec![None; self.slots_len()])
104
57.5k
    }
105
106
    /// Returns the position of the next character after `i`.
107
    ///
108
    /// For example, a haystack with type `&[u8]` probably returns `i+1`,
109
    /// whereas a haystack with type `&str` probably returns `i` plus the
110
    /// length of the next UTF-8 sequence.
111
    fn next_after_empty(&self, text: &Self::Text, i: usize) -> usize;
112
113
    /// Returns the location of the shortest match.
114
    fn shortest_match_at(
115
        &self,
116
        text: &Self::Text,
117
        start: usize,
118
    ) -> Option<usize>;
119
120
    /// Returns whether the regex matches the text given.
121
    fn is_match_at(&self, text: &Self::Text, start: usize) -> bool;
122
123
    /// Returns the leftmost-first match location if one exists.
124
    fn find_at(
125
        &self,
126
        text: &Self::Text,
127
        start: usize,
128
    ) -> Option<(usize, usize)>;
129
130
    /// Returns the leftmost-first match location if one exists, and also
131
    /// fills in any matching capture slot locations.
132
    fn captures_read_at(
133
        &self,
134
        locs: &mut Locations,
135
        text: &Self::Text,
136
        start: usize,
137
    ) -> Option<(usize, usize)>;
138
139
    /// Returns an iterator over all non-overlapping successive leftmost-first
140
    /// matches.
141
0
    fn find_iter(self, text: &Self::Text) -> Matches<'_, Self> {
142
0
        Matches { re: self, text: text, last_end: 0, last_match: None }
143
0
    }
Unexecuted instantiation: <regex::exec::ExecNoSync as regex::re_trait::RegularExpression>::find_iter
Unexecuted instantiation: <regex::exec::ExecNoSyncStr as regex::re_trait::RegularExpression>::find_iter
Unexecuted instantiation: <regex::exec::ExecNoSync as regex::re_trait::RegularExpression>::find_iter
Unexecuted instantiation: <regex::exec::ExecNoSyncStr as regex::re_trait::RegularExpression>::find_iter
144
145
    /// Returns an iterator over all non-overlapping successive leftmost-first
146
    /// matches with captures.
147
0
    fn captures_iter(self, text: &Self::Text) -> CaptureMatches<'_, Self> {
148
0
        CaptureMatches(self.find_iter(text))
149
0
    }
Unexecuted instantiation: <regex::exec::ExecNoSync as regex::re_trait::RegularExpression>::captures_iter
Unexecuted instantiation: <regex::exec::ExecNoSyncStr as regex::re_trait::RegularExpression>::captures_iter
Unexecuted instantiation: <regex::exec::ExecNoSync as regex::re_trait::RegularExpression>::captures_iter
Unexecuted instantiation: <regex::exec::ExecNoSyncStr as regex::re_trait::RegularExpression>::captures_iter
150
}
151
152
/// An iterator over all non-overlapping successive leftmost-first matches.
153
#[derive(Debug)]
154
pub struct Matches<'t, R>
155
where
156
    R: RegularExpression,
157
    R::Text: 't,
158
{
159
    re: R,
160
    text: &'t R::Text,
161
    last_end: usize,
162
    last_match: Option<usize>,
163
}
164
165
impl<'t, R> Matches<'t, R>
166
where
167
    R: RegularExpression,
168
    R::Text: 't,
169
{
170
    /// Return the text being searched.
171
0
    pub fn text(&self) -> &'t R::Text {
172
0
        self.text
173
0
    }
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSync>>::text
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSyncStr>>::text
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSync>>::text
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSyncStr>>::text
174
175
    /// Return the underlying regex.
176
0
    pub fn regex(&self) -> &R {
177
0
        &self.re
178
0
    }
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSync>>::regex
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSyncStr>>::regex
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSync>>::regex
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSyncStr>>::regex
179
}
180
181
impl<'t, R> Iterator for Matches<'t, R>
182
where
183
    R: RegularExpression,
184
    R::Text: 't + AsRef<[u8]>,
185
{
186
    type Item = (usize, usize);
187
188
0
    fn next(&mut self) -> Option<(usize, usize)> {
189
0
        if self.last_end > self.text.as_ref().len() {
190
0
            return None;
191
0
        }
192
0
        let (s, e) = match self.re.find_at(self.text, self.last_end) {
193
0
            None => return None,
194
0
            Some((s, e)) => (s, e),
195
        };
196
0
        if s == e {
197
            // This is an empty match. To ensure we make progress, start
198
            // the next search at the smallest possible starting position
199
            // of the next match following this one.
200
0
            self.last_end = self.re.next_after_empty(self.text, e);
201
            // Don't accept empty matches immediately following a match.
202
            // Just move on to the next match.
203
0
            if Some(e) == self.last_match {
204
0
                return self.next();
205
0
            }
206
0
        } else {
207
0
            self.last_end = e;
208
0
        }
209
0
        self.last_match = Some(e);
210
0
        Some((s, e))
211
0
    }
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSync> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSyncStr> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSync> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::Matches<regex::exec::ExecNoSyncStr> as core::iter::traits::iterator::Iterator>::next
212
}
213
214
impl<'t, R> FusedIterator for Matches<'t, R>
215
where
216
    R: RegularExpression,
217
    R::Text: 't + AsRef<[u8]>,
218
{
219
}
220
221
/// An iterator over all non-overlapping successive leftmost-first matches with
222
/// captures.
223
#[derive(Debug)]
224
pub struct CaptureMatches<'t, R>(Matches<'t, R>)
225
where
226
    R: RegularExpression,
227
    R::Text: 't;
228
229
impl<'t, R> CaptureMatches<'t, R>
230
where
231
    R: RegularExpression,
232
    R::Text: 't,
233
{
234
    /// Return the text being searched.
235
0
    pub fn text(&self) -> &'t R::Text {
236
0
        self.0.text()
237
0
    }
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSync>>::text
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSyncStr>>::text
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSync>>::text
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSyncStr>>::text
238
239
    /// Return the underlying regex.
240
0
    pub fn regex(&self) -> &R {
241
0
        self.0.regex()
242
0
    }
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSync>>::regex
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSyncStr>>::regex
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSync>>::regex
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSyncStr>>::regex
243
}
244
245
impl<'t, R> Iterator for CaptureMatches<'t, R>
246
where
247
    R: RegularExpression,
248
    R::Text: 't + AsRef<[u8]>,
249
{
250
    type Item = Locations;
251
252
0
    fn next(&mut self) -> Option<Locations> {
253
0
        if self.0.last_end > self.0.text.as_ref().len() {
254
0
            return None;
255
0
        }
256
0
        let mut locs = self.0.re.locations();
257
0
        let (s, e) = match self.0.re.captures_read_at(
258
0
            &mut locs,
259
0
            self.0.text,
260
0
            self.0.last_end,
261
0
        ) {
262
0
            None => return None,
263
0
            Some((s, e)) => (s, e),
264
        };
265
0
        if s == e {
266
0
            self.0.last_end = self.0.re.next_after_empty(self.0.text, e);
267
0
            if Some(e) == self.0.last_match {
268
0
                return self.next();
269
0
            }
270
0
        } else {
271
0
            self.0.last_end = e;
272
0
        }
273
0
        self.0.last_match = Some(e);
274
0
        Some(locs)
275
0
    }
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSync> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSyncStr> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSync> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <regex::re_trait::CaptureMatches<regex::exec::ExecNoSyncStr> as core::iter::traits::iterator::Iterator>::next
276
}
277
278
impl<'t, R> FusedIterator for CaptureMatches<'t, R>
279
where
280
    R: RegularExpression,
281
    R::Text: 't + AsRef<[u8]>,
282
{
283
}