safe_regex/
lib.rs

1//! [![crates.io version](https://img.shields.io/crates/v/safe-regex.svg)](https://crates.io/crates/safe-regex)
2//! [![license: Apache 2.0](https://gitlab.com/leonhard-llc/safe-regex-rs/-/raw/main/license-apache-2.0.svg)](http://www.apache.org/licenses/LICENSE-2.0)
3//! [![unsafe forbidden](https://gitlab.com/leonhard-llc/safe-regex-rs/-/raw/main/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)
4//! [![pipeline status](https://gitlab.com/leonhard-llc/safe-regex-rs/badges/main/pipeline.svg)](https://gitlab.com/leonhard-llc/safe-regex-rs/-/pipelines)
5//!
6//! A safe regular expression library.
7//!
8//! # Features
9//! - `forbid(unsafe_code)`
10//! - Good test coverage (~80%)
11//! - Runtime is linear.
12//! - Memory usage is constant.  Does not allocate.
13//! - Compiles your regular expression to a simple Rust function
14//! - Rust compiler checks and optimizes the matcher
15//! - Supports basic regular expression syntax:
16//!   - Any byte: `.`
17//!   - Sequences: `abc`
18//!   - Classes: `[-ab0-9]`, `[^ab]`
19//!   - Repetition: `a?`, `a*`, `a+`, `a{1}`, `a{1,}`, `a{,1}`, `a{1,2}`, `a{,}`
20//!   - Alternates: `a|b|c`
21//!   - Capturing groups: `a(bc)?`
22//!   - Non-capturing groups: `a(?:bc)?`
23//! - `no_std`, by omitting the default `"std"` feature
24//!
25//! # Limitations
26//! - Only works on byte slices, not strings.
27//! - Partially optimized.  Runtime is about 10 times slower than
28//!   [`regex`](https://crates.io/crates/regex) crate.
29//!   Here are relative runtimes measured with
30//!   [`safe-regex-rs/bench`](https://gitlab.com/leonhard-llc/safe-regex-rs/-/tree/main/bench)
31//!   run on a 2018 Macbook Pro:
32//!
33//!   | `regex` | `safe_regex` | expression |
34//!   | ----- | ---------- | ---------- |
35//!   | 1 | 6 | find phone num `.*([0-9]{3})[-. ]?([0-9]{3})[-. ]?([0-9]{4}).*` |
36//!   | 1 | 20 | find date time `.*([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+).*` |
37//!   | 1 | 0.75 | parse date time `([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+)` |
38//!   | 1 | 50 | check PEM Base64 `[a-zA-Z0-9+/]{0,64}=*` |
39//!   | 1 | 20-500 | substring search `.*(2G8H81RFNZ).*` |
40//!
41//! # Alternatives
42//! - [`regex`](https://crates.io/crates/regex)
43//!   - Mature & Popular
44//!   - Maintained by the core Rust language developers
45//!   - Contains `unsafe` code.
46//!   - Allocates
47//!   - Compiles your regular expression at runtime at first use.
48//!   - Subsequent uses must retrieve it from the cache.
49//! - [`pcre2`](https://crates.io/crates/pcre2)
50//!   - Uses PCRE library which is written in unsafe C.
51//! - [`regular-expression`](https://crates.io/crates/regular-expression)
52//!   - No documentation
53//! - [`rec`](https://crates.io/crates/rec)
54//!
55//! # Cargo Geiger Safety Report
56//! # Examples
57//! ```rust
58//! use safe_regex::{regex, Matcher0};
59//! let matcher: Matcher0<_> =
60//!     regex!(br"[ab][0-9]*");
61//! assert!(matcher.is_match(b"a42"));
62//! assert!(!matcher.is_match(b"X"));
63//! ```
64//!
65//! ```rust
66//! use safe_regex::{regex, Matcher3};
67//! let matcher: Matcher3<_> =
68//!     regex!(br"([ab])([0-9]*)(suffix)?");
69//! let (prefix, digits, suffix) =
70//!     matcher.match_slices(b"a42").unwrap();
71//! assert_eq!(b"a", prefix);
72//! assert_eq!(b"42", digits);
73//! assert_eq!(b"", suffix);
74//! let (prefix_range, digits_r, suffix_r)
75//!     = matcher.match_ranges(b"a42").unwrap();
76//! assert_eq!(0..1_usize, prefix_range);
77//! assert_eq!(1..3_usize, digits_r);
78//! assert_eq!(0..0_usize, suffix_r);
79//! ```
80//!
81//! # Changelog
82//! - v0.3.0 - Add `assert_match` and default `std` feature.
83//! - v0.2.6 - Fix some Clippy warnings on `regex!` macro invocation sites.
84//! - v0.2.5 - Fix `no_std`.  Thank you, Soares Chen! [github.com/soareschen](https://github.com/soareschen) [gitlab.com/soareschen-informal](https://gitlab.com/soareschen-informal)
85//! - v0.2.4
86//!   - Bug fixes, reducing performance.
87//!   - Optimize non-match runtime.
88//! - v0.2.3
89//!   - Rename `match_all` -> `match_slices`.
90//!   - Add `match_ranges`.
91//! - v0.2.2 - Simplify `match_all` return type
92//! - v0.2.1 - Non-capturing groups, bug fixes
93//! - v0.2.0
94//!   - Linear-time & constant-memory algorithm! :)
95//!   - Work around rustc optimizer hang on regexes with exponential execution paths like "a{,30}".
96//!     See `src/bin/uncompilable/main.rs`.
97//! - v0.1.1 - Bug fixes and more tests.
98//! - v0.1.0 - First published version
99//!
100//! # TO DO
101//! - 11+ capturing groups
102//! - Increase coverage
103//! - Add fuzzing tests
104//! - Common character classes: whitespace, letters, punctuation, etc.
105//! - Match strings
106//! - Repeated capturing groups: `(ab|cd)*`.
107//!   Idea: Return an `MatcherNIter` struct that is an iterator that returns `MatcherN` structs.
108//! - Implement optimizations explained in <https://swtch.com/%7Ersc/regexp/regexp3.html> .
109//!   Some of the code already exists in `tests/dfa_single_pass.rs`
110//!   and `tests/nfa_without_capturing.rs`.
111//! - Once [const generics](https://github.com/rust-lang/rust/issues/44580)
112//!   are stable, use the feature to simplify some types.
113//! - Once
114//!   [trait bounds on `const fn` parameters are stable](https://github.com/rust-lang/rust/issues/57563),
115//!   make the `MatcherN::new` functions `const`.
116//!
117//! # Development
118//! - An overview of how this library works:
119//!   <https://news.ycombinator.com/item?id=27301320>
120
121// https://swtch.com/~rsc/regexp/regexp1.html
122// https://compiler.org/reason-re-nfa/src/index.html
123
124#![no_std]
125#![forbid(unsafe_code)]
126#![allow(clippy::type_complexity)]
127
128use core::ops::Range;
129pub use safe_regex_macro::regex;
130
131#[cfg(feature = "std")]
132extern crate std;
133
134#[cfg(feature = "std")]
135fn escape_ascii(input: impl AsRef<[u8]>) -> std::string::String {
136    let mut result = std::string::String::new();
137    for byte in input.as_ref() {
138        for ascii_byte in core::ascii::escape_default(*byte) {
139            result.push_str(core::str::from_utf8(&[ascii_byte]).unwrap());
140        }
141    }
142    result
143}
144
145/// Provides an `is_match` function.
146pub trait IsMatch {
147    /// Returns `true` if `data` matches the regular expression,
148    /// otherwise returns `false`.
149    ///
150    /// This is a whole-string match.
151    /// For sub-string search, put `.*` at the beginning and end of the regex.
152    fn is_match(&self, data: &[u8]) -> bool;
153
154    /// # Panics
155    /// Panics when `data` does match the regular expression.
156    fn assert_match(&self, data: &[u8]) {
157        #[cfg(feature = "std")]
158        assert!(
159            self.is_match(data),
160            "bytes do not match regular expression: b'{}'",
161            escape_ascii(data)
162        );
163        #[cfg(not(feature = "std"))]
164        assert!(
165            self.is_match(data),
166            "bytes do not match regular expression: {:?}",
167            data
168        );
169    }
170
171    /// # Panics
172    /// Panics when `data` does not match the regular expression.
173    fn assert_no_match(&self, data: &[u8]) {
174        #[cfg(feature = "std")]
175        assert!(
176            !self.is_match(data),
177            "bytes match regular expression: b'{}'",
178            escape_ascii(data)
179        );
180        #[cfg(not(feature = "std"))]
181        assert!(
182            !self.is_match(data),
183            "bytes match regular expression: {:?}",
184            data
185        );
186    }
187}
188
189/// A compiled regular expression with no capturing groups.
190///
191/// This is a zero-length type.
192/// The `regex!` macro generates a Rust type that implements the regular expression.
193/// This struct holds that type.
194pub struct Matcher0<F>
195where
196    F: Fn(&[u8]) -> Option<()>,
197{
198    f: F,
199}
200impl<F> Matcher0<F>
201where
202    F: Fn(&[u8]) -> Option<()>,
203{
204    /// This is used internally by the `regex!` macro.
205    #[must_use]
206    pub fn new(f: F) -> Self {
207        Self { f }
208    }
209
210    /// Returns `true` if `data` matches the regular expression,
211    /// otherwise returns `false`.
212    ///
213    /// This is a whole-string match.
214    /// For sub-string search, put `.*` at the beginning and end of the regex.
215    ///
216    /// # Example
217    /// ```rust
218    /// use safe_regex::{regex, Matcher0};
219    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
220    /// assert!(matcher.is_match(b"a42"));
221    /// assert!(!matcher.is_match(b"X"));
222    /// ```
223    #[must_use]
224    pub fn is_match(&self, data: &[u8]) -> bool {
225        (self.f)(data).is_some()
226    }
227
228    /// Executes the regular expression against the byte slice `data`.
229    ///
230    /// Returns `Some((&[u8],&[u8],...))`
231    /// if the expression matched all of the bytes in `data`.
232    /// The tuple fields are slices of `data` that matched
233    /// capturing groups in the expression.
234    ///
235    /// This is a whole-string match.
236    /// For sub-string search, put `.*` at the beginning and end of the regex.
237    ///
238    /// Returns `None` if the expression did not match `data`.
239    ///
240    /// # Example
241    /// ```rust
242    /// use safe_regex::{regex, Matcher3};
243    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
244    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
245    /// assert_eq!(b"a", prefix);
246    /// assert_eq!(b"42", digits);
247    /// assert!(suffix.is_empty());
248    /// ```
249    #[must_use]
250    pub fn match_slices(&self, data: &[u8]) -> Option<()> {
251        (self.f)(data)
252    }
253
254    /// Executes the regular expression against the byte slice `data`.
255    ///
256    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
257    /// The tuple fields are ranges of bytes in `data` that matched capturing
258    /// groups in the expression.
259    /// A capturing group that matches no bytes will produce as a zero-length
260    /// range.
261    ///
262    /// This is a whole-string match.
263    /// For sub-string search, put `.*` at the beginning and end of the regex.
264    ///
265    /// Returns `None` if the expression did not match `data`.
266    ///
267    /// # Example
268    /// ```rust
269    /// use safe_regex::{regex, Matcher3};
270    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
271    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
272    /// assert_eq!(0..1_usize, prefix);
273    /// assert_eq!(1..3_usize, digits);
274    /// assert_eq!(0..0_usize, suffix);
275    /// ```
276    #[must_use]
277    pub fn match_ranges(&self, data: &[u8]) -> Option<()> {
278        (self.f)(data)
279    }
280}
281impl<F: Fn(&[u8]) -> Option<()>> IsMatch for Matcher0<F> {
282    fn is_match(&self, data: &[u8]) -> bool {
283        self.is_match(data)
284    }
285}
286
287/// A compiled regular expression with 1 capturing groups.
288///
289/// This is a zero-length type.
290/// The `regex!` macro generates a Rust type that implements the regular expression.
291/// This struct holds that type.
292pub struct Matcher1<F>
293where
294    F: Fn(&[u8]) -> Option<[Range<usize>; 1]>,
295{
296    f: F,
297}
298impl<F> Matcher1<F>
299where
300    F: Fn(&[u8]) -> Option<[Range<usize>; 1]>,
301{
302    /// This is used internally by the `regex!` macro.
303    #[must_use]
304    pub fn new(f: F) -> Self {
305        Self { f }
306    }
307
308    /// Returns `true` if `data` matches the regular expression,
309    /// otherwise returns `false`.
310    ///
311    /// This is a whole-string match.
312    /// For sub-string search, put `.*` at the beginning and end of the regex.
313    ///
314    /// # Example
315    /// ```rust
316    /// use safe_regex::{regex, Matcher0};
317    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
318    /// assert!(matcher.is_match(b"a42"));
319    /// assert!(!matcher.is_match(b"X"));
320    /// ```
321    #[must_use]
322    pub fn is_match(&self, data: &[u8]) -> bool {
323        (self.f)(data).is_some()
324    }
325
326    /// Executes the regular expression against the byte slice `data`.
327    ///
328    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
329    /// The tuple fields are ranges of bytes in `data` that matched capturing
330    /// groups in the expression.
331    /// A capturing group that matches no bytes will produce as a zero-length
332    /// range.
333    ///
334    /// This is a whole-string match.
335    /// For sub-string search, put `.*` at the beginning and end of the regex.
336    ///
337    /// Returns `None` if the expression did not match `data`.
338    ///
339    /// # Example
340    /// ```rust
341    /// use safe_regex::{regex, Matcher3};
342    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
343    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
344    /// assert_eq!(0..1_usize, prefix);
345    /// assert_eq!(1..3_usize, digits);
346    /// assert_eq!(0..0_usize, suffix);
347    /// ```
348    #[must_use]
349    pub fn match_ranges(&self, data: &[u8]) -> Option<(Range<usize>,)> {
350        let [r0] = (self.f)(data)?;
351        Some((r0,))
352    }
353
354    /// Executes the regular expression against the byte slice `data`.
355    ///
356    /// Returns `Some((&[u8],&[u8],...))`
357    /// if the expression matched all of the bytes in `data`.
358    /// The tuple fields are slices of `data` that matched
359    /// capturing groups in the expression.
360    ///
361    /// This is a whole-string match.
362    /// For sub-string search, put `.*` at the beginning and end of the regex.
363    ///
364    /// Returns `None` if the expression did not match `data`.
365    ///
366    /// # Example
367    /// ```rust
368    /// use safe_regex::{regex, Matcher3};
369    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
370    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
371    /// assert_eq!(b"a", prefix);
372    /// assert_eq!(b"42", digits);
373    /// assert!(suffix.is_empty());
374    /// ```
375    #[must_use]
376    pub fn match_slices<'d>(&self, data: &'d [u8]) -> Option<(&'d [u8],)> {
377        let [r0] = (self.f)(data)?;
378        Some((&data[r0],))
379    }
380}
381impl<F: Fn(&[u8]) -> Option<[Range<usize>; 1]>> IsMatch for Matcher1<F> {
382    fn is_match(&self, data: &[u8]) -> bool {
383        self.is_match(data)
384    }
385}
386
387/// A compiled regular expression with 2 capturing groups.
388///
389/// This is a zero-length type.
390/// The `regex!` macro generates a Rust type that implements the regular expression.
391/// This struct holds that type.
392pub struct Matcher2<F>
393where
394    F: Fn(&[u8]) -> Option<[Range<usize>; 2]>,
395{
396    f: F,
397}
398impl<F> Matcher2<F>
399where
400    F: Fn(&[u8]) -> Option<[Range<usize>; 2]>,
401{
402    /// This is used internally by the `regex!` macro.
403    #[must_use]
404    pub fn new(f: F) -> Self {
405        Self { f }
406    }
407
408    /// Returns `true` if `data` matches the regular expression,
409    /// otherwise returns `false`.
410    ///
411    /// This is a whole-string match.
412    /// For sub-string search, put `.*` at the beginning and end of the regex.
413    ///
414    /// # Example
415    /// ```rust
416    /// use safe_regex::{regex, Matcher0};
417    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
418    /// assert!(matcher.is_match(b"a42"));
419    /// assert!(!matcher.is_match(b"X"));
420    /// ```
421    #[must_use]
422    pub fn is_match(&self, data: &[u8]) -> bool {
423        (self.f)(data).is_some()
424    }
425
426    /// Executes the regular expression against the byte slice `data`.
427    ///
428    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
429    /// The tuple fields are ranges of bytes in `data` that matched capturing
430    /// groups in the expression.
431    /// A capturing group that matches no bytes will produce as a zero-length
432    /// range.
433    ///
434    /// This is a whole-string match.
435    /// For sub-string search, put `.*` at the beginning and end of the regex.
436    ///
437    /// Returns `None` if the expression did not match `data`.
438    ///
439    /// # Example
440    /// ```rust
441    /// use safe_regex::{regex, Matcher3};
442    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
443    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
444    /// assert_eq!(0..1_usize, prefix);
445    /// assert_eq!(1..3_usize, digits);
446    /// assert_eq!(0..0_usize, suffix);
447    /// ```
448    #[must_use]
449    pub fn match_ranges(&self, data: &[u8]) -> Option<(Range<usize>, Range<usize>)> {
450        let [r0, r1] = (self.f)(data)?;
451        Some((r0, r1))
452    }
453
454    /// Executes the regular expression against the byte slice `data`.
455    ///
456    /// Returns `Some((&[u8],&[u8],...))`
457    /// if the expression matched all of the bytes in `data`.
458    /// The tuple fields are slices of `data` that matched
459    /// capturing groups in the expression.
460    ///
461    /// This is a whole-string match.
462    /// For sub-string search, put `.*` at the beginning and end of the regex.
463    ///
464    /// Returns `None` if the expression did not match `data`.
465    ///
466    /// # Example
467    /// ```rust
468    /// use safe_regex::{regex, Matcher3};
469    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
470    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
471    /// assert_eq!(b"a", prefix);
472    /// assert_eq!(b"42", digits);
473    /// assert!(suffix.is_empty());
474    /// ```
475    #[must_use]
476    pub fn match_slices<'d>(&self, data: &'d [u8]) -> Option<(&'d [u8], &'d [u8])> {
477        let [r0, r1] = (self.f)(data)?;
478        Some((&data[r0], &data[r1]))
479    }
480}
481impl<F: Fn(&[u8]) -> Option<[Range<usize>; 2]>> IsMatch for Matcher2<F> {
482    fn is_match(&self, data: &[u8]) -> bool {
483        self.is_match(data)
484    }
485}
486
487/// A compiled regular expression with 3 capturing groups.
488///
489/// This is a zero-length type.
490/// The `regex!` macro generates a Rust type that implements the regular expression.
491/// This struct holds that type.
492pub struct Matcher3<F>
493where
494    F: Fn(&[u8]) -> Option<[Range<usize>; 3]>,
495{
496    f: F,
497}
498impl<F> Matcher3<F>
499where
500    F: Fn(&[u8]) -> Option<[Range<usize>; 3]>,
501{
502    /// This is used internally by the `regex!` macro.
503    #[must_use]
504    pub fn new(f: F) -> Self {
505        Self { f }
506    }
507
508    /// Returns `true` if `data` matches the regular expression,
509    /// otherwise returns `false`.
510    ///
511    /// This is a whole-string match.
512    /// For sub-string search, put `.*` at the beginning and end of the regex.
513    ///
514    /// # Example
515    /// ```rust
516    /// use safe_regex::{regex, Matcher0};
517    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
518    /// assert!(matcher.is_match(b"a42"));
519    /// assert!(!matcher.is_match(b"X"));
520    /// ```
521    #[must_use]
522    pub fn is_match(&self, data: &[u8]) -> bool {
523        (self.f)(data).is_some()
524    }
525
526    /// Executes the regular expression against the byte slice `data`.
527    ///
528    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
529    /// The tuple fields are ranges of bytes in `data` that matched capturing
530    /// groups in the expression.
531    /// A capturing group that matches no bytes will produce as a zero-length
532    /// range.
533    ///
534    /// This is a whole-string match.
535    /// For sub-string search, put `.*` at the beginning and end of the regex.
536    ///
537    /// Returns `None` if the expression did not match `data`.
538    ///
539    /// # Example
540    /// ```rust
541    /// use safe_regex::{regex, Matcher3};
542    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
543    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
544    /// assert_eq!(0..1_usize, prefix);
545    /// assert_eq!(1..3_usize, digits);
546    /// assert_eq!(0..0_usize, suffix);
547    /// ```
548    #[must_use]
549    pub fn match_ranges(&self, data: &[u8]) -> Option<(Range<usize>, Range<usize>, Range<usize>)> {
550        let [r0, r1, r2] = (self.f)(data)?;
551        Some((r0, r1, r2))
552    }
553
554    /// Executes the regular expression against the byte slice `data`.
555    ///
556    /// Returns `Some((&[u8],&[u8],...))`
557    /// if the expression matched all of the bytes in `data`.
558    /// The tuple fields are slices of `data` that matched
559    /// capturing groups in the expression.
560    ///
561    /// This is a whole-string match.
562    /// For sub-string search, put `.*` at the beginning and end of the regex.
563    ///
564    /// Returns `None` if the expression did not match `data`.
565    ///
566    /// # Example
567    /// ```rust
568    /// use safe_regex::{regex, Matcher3};
569    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
570    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
571    /// assert_eq!(b"a", prefix);
572    /// assert_eq!(b"42", digits);
573    /// assert!(suffix.is_empty());
574    /// ```
575    #[must_use]
576    pub fn match_slices<'d>(&self, data: &'d [u8]) -> Option<(&'d [u8], &'d [u8], &'d [u8])> {
577        let [r0, r1, r2] = (self.f)(data)?;
578        Some((&data[r0], &data[r1], &data[r2]))
579    }
580}
581impl<F: Fn(&[u8]) -> Option<[Range<usize>; 3]>> IsMatch for Matcher3<F> {
582    fn is_match(&self, data: &[u8]) -> bool {
583        self.is_match(data)
584    }
585}
586
587/// A compiled regular expression with 4 capturing groups.
588///
589/// This is a zero-length type.
590/// The `regex!` macro generates a Rust type that implements the regular expression.
591/// This struct holds that type.
592pub struct Matcher4<F>
593where
594    F: Fn(&[u8]) -> Option<[Range<usize>; 4]>,
595{
596    f: F,
597}
598impl<F> Matcher4<F>
599where
600    F: Fn(&[u8]) -> Option<[Range<usize>; 4]>,
601{
602    /// This is used internally by the `regex!` macro.
603    #[must_use]
604    pub fn new(f: F) -> Self {
605        Self { f }
606    }
607
608    /// Returns `true` if `data` matches the regular expression,
609    /// otherwise returns `false`.
610    ///
611    /// This is a whole-string match.
612    /// For sub-string search, put `.*` at the beginning and end of the regex.
613    ///
614    /// # Example
615    /// ```rust
616    /// use safe_regex::{regex, Matcher0};
617    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
618    /// assert!(matcher.is_match(b"a42"));
619    /// assert!(!matcher.is_match(b"X"));
620    /// ```
621    #[must_use]
622    pub fn is_match(&self, data: &[u8]) -> bool {
623        (self.f)(data).is_some()
624    }
625
626    /// Executes the regular expression against the byte slice `data`.
627    ///
628    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
629    /// The tuple fields are ranges of bytes in `data` that matched capturing
630    /// groups in the expression.
631    /// A capturing group that matches no bytes will produce as a zero-length
632    /// range.
633    ///
634    /// This is a whole-string match.
635    /// For sub-string search, put `.*` at the beginning and end of the regex.
636    ///
637    /// Returns `None` if the expression did not match `data`.
638    ///
639    /// # Example
640    /// ```rust
641    /// use safe_regex::{regex, Matcher3};
642    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
643    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
644    /// assert_eq!(0..1_usize, prefix);
645    /// assert_eq!(1..3_usize, digits);
646    /// assert_eq!(0..0_usize, suffix);
647    /// ```
648    #[must_use]
649    pub fn match_ranges(
650        &self,
651        data: &[u8],
652    ) -> Option<(Range<usize>, Range<usize>, Range<usize>, Range<usize>)> {
653        let [r0, r1, r2, r3] = (self.f)(data)?;
654        Some((r0, r1, r2, r3))
655    }
656
657    /// Executes the regular expression against the byte slice `data`.
658    ///
659    /// Returns `Some((&[u8],&[u8],...))`
660    /// if the expression matched all of the bytes in `data`.
661    /// The tuple fields are slices of `data` that matched
662    /// capturing groups in the expression.
663    ///
664    /// This is a whole-string match.
665    /// For sub-string search, put `.*` at the beginning and end of the regex.
666    ///
667    /// Returns `None` if the expression did not match `data`.
668    ///
669    /// # Example
670    /// ```rust
671    /// use safe_regex::{regex, Matcher3};
672    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
673    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
674    /// assert_eq!(b"a", prefix);
675    /// assert_eq!(b"42", digits);
676    /// assert!(suffix.is_empty());
677    /// ```
678    #[must_use]
679    pub fn match_slices<'d>(
680        &self,
681        data: &'d [u8],
682    ) -> Option<(&'d [u8], &'d [u8], &'d [u8], &'d [u8])> {
683        let [r0, r1, r2, r3] = (self.f)(data)?;
684        Some((&data[r0], &data[r1], &data[r2], &data[r3]))
685    }
686}
687impl<F: Fn(&[u8]) -> Option<[Range<usize>; 4]>> IsMatch for Matcher4<F> {
688    fn is_match(&self, data: &[u8]) -> bool {
689        self.is_match(data)
690    }
691}
692
693/// A compiled regular expression with 5 capturing groups.
694///
695/// This is a zero-length type.
696/// The `regex!` macro generates a Rust type that implements the regular expression.
697/// This struct holds that type.
698pub struct Matcher5<F>
699where
700    F: Fn(&[u8]) -> Option<[Range<usize>; 5]>,
701{
702    f: F,
703}
704impl<F> Matcher5<F>
705where
706    F: Fn(&[u8]) -> Option<[Range<usize>; 5]>,
707{
708    /// This is used internally by the `regex!` macro.
709    #[must_use]
710    pub fn new(f: F) -> Self {
711        Self { f }
712    }
713
714    /// Returns `true` if `data` matches the regular expression,
715    /// otherwise returns `false`.
716    ///
717    /// This is a whole-string match.
718    /// For sub-string search, put `.*` at the beginning and end of the regex.
719    ///
720    /// # Example
721    /// ```rust
722    /// use safe_regex::{regex, Matcher0};
723    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
724    /// assert!(matcher.is_match(b"a42"));
725    /// assert!(!matcher.is_match(b"X"));
726    /// ```
727    #[must_use]
728    pub fn is_match(&self, data: &[u8]) -> bool {
729        (self.f)(data).is_some()
730    }
731
732    /// Executes the regular expression against the byte slice `data`.
733    ///
734    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
735    /// The tuple fields are ranges of bytes in `data` that matched capturing
736    /// groups in the expression.
737    /// A capturing group that matches no bytes will produce as a zero-length
738    /// range.
739    ///
740    /// This is a whole-string match.
741    /// For sub-string search, put `.*` at the beginning and end of the regex.
742    ///
743    /// Returns `None` if the expression did not match `data`.
744    ///
745    /// # Example
746    /// ```rust
747    /// use safe_regex::{regex, Matcher3};
748    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
749    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
750    /// assert_eq!(0..1_usize, prefix);
751    /// assert_eq!(1..3_usize, digits);
752    /// assert_eq!(0..0_usize, suffix);
753    /// ```
754    #[must_use]
755    pub fn match_ranges(
756        &self,
757        data: &[u8],
758    ) -> Option<(
759        Range<usize>,
760        Range<usize>,
761        Range<usize>,
762        Range<usize>,
763        Range<usize>,
764    )> {
765        let [r0, r1, r2, r3, r4] = (self.f)(data)?;
766        Some((r0, r1, r2, r3, r4))
767    }
768
769    /// Executes the regular expression against the byte slice `data`.
770    ///
771    /// Returns `Some((&[u8],&[u8],...))`
772    /// if the expression matched all of the bytes in `data`.
773    /// The tuple fields are slices of `data` that matched
774    /// capturing groups in the expression.
775    ///
776    /// This is a whole-string match.
777    /// For sub-string search, put `.*` at the beginning and end of the regex.
778    ///
779    /// Returns `None` if the expression did not match `data`.
780    ///
781    /// # Example
782    /// ```rust
783    /// use safe_regex::{regex, Matcher3};
784    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
785    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
786    /// assert_eq!(b"a", prefix);
787    /// assert_eq!(b"42", digits);
788    /// assert!(suffix.is_empty());
789    /// ```
790    #[must_use]
791    pub fn match_slices<'d>(
792        &self,
793        data: &'d [u8],
794    ) -> Option<(&'d [u8], &'d [u8], &'d [u8], &'d [u8], &'d [u8])> {
795        let [r0, r1, r2, r3, r4] = (self.f)(data)?;
796        Some((&data[r0], &data[r1], &data[r2], &data[r3], &data[r4]))
797    }
798}
799impl<F: Fn(&[u8]) -> Option<[Range<usize>; 5]>> IsMatch for Matcher5<F> {
800    fn is_match(&self, data: &[u8]) -> bool {
801        self.is_match(data)
802    }
803}
804
805/// A compiled regular expression with 6 capturing groups.
806///
807/// This is a zero-length type.
808/// The `regex!` macro generates a Rust type that implements the regular expression.
809/// This struct holds that type.
810pub struct Matcher6<F>
811where
812    F: Fn(&[u8]) -> Option<[Range<usize>; 6]>,
813{
814    f: F,
815}
816impl<F> Matcher6<F>
817where
818    F: Fn(&[u8]) -> Option<[Range<usize>; 6]>,
819{
820    /// This is used internally by the `regex!` macro.
821    #[must_use]
822    pub fn new(f: F) -> Self {
823        Self { f }
824    }
825
826    /// Returns `true` if `data` matches the regular expression,
827    /// otherwise returns `false`.
828    ///
829    /// This is a whole-string match.
830    /// For sub-string search, put `.*` at the beginning and end of the regex.
831    ///
832    /// # Example
833    /// ```rust
834    /// use safe_regex::{regex, Matcher0};
835    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
836    /// assert!(matcher.is_match(b"a42"));
837    /// assert!(!matcher.is_match(b"X"));
838    /// ```
839    #[must_use]
840    pub fn is_match(&self, data: &[u8]) -> bool {
841        (self.f)(data).is_some()
842    }
843
844    /// Executes the regular expression against the byte slice `data`.
845    ///
846    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
847    /// The tuple fields are ranges of bytes in `data` that matched capturing
848    /// groups in the expression.
849    /// A capturing group that matches no bytes will produce as a zero-length
850    /// range.
851    ///
852    /// This is a whole-string match.
853    /// For sub-string search, put `.*` at the beginning and end of the regex.
854    ///
855    /// Returns `None` if the expression did not match `data`.
856    ///
857    /// # Example
858    /// ```rust
859    /// use safe_regex::{regex, Matcher3};
860    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
861    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
862    /// assert_eq!(0..1_usize, prefix);
863    /// assert_eq!(1..3_usize, digits);
864    /// assert_eq!(0..0_usize, suffix);
865    /// ```
866    #[must_use]
867    pub fn match_ranges(
868        &self,
869        data: &[u8],
870    ) -> Option<(
871        Range<usize>,
872        Range<usize>,
873        Range<usize>,
874        Range<usize>,
875        Range<usize>,
876        Range<usize>,
877    )> {
878        let [r0, r1, r2, r3, r4, r5] = (self.f)(data)?;
879        Some((r0, r1, r2, r3, r4, r5))
880    }
881
882    /// Executes the regular expression against the byte slice `data`.
883    ///
884    /// Returns `Some((&[u8],&[u8],...))`
885    /// if the expression matched all of the bytes in `data`.
886    /// The tuple fields are slices of `data` that matched
887    /// capturing groups in the expression.
888    ///
889    /// This is a whole-string match.
890    /// For sub-string search, put `.*` at the beginning and end of the regex.
891    ///
892    /// Returns `None` if the expression did not match `data`.
893    ///
894    /// # Example
895    /// ```rust
896    /// use safe_regex::{regex, Matcher3};
897    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
898    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
899    /// assert_eq!(b"a", prefix);
900    /// assert_eq!(b"42", digits);
901    /// assert!(suffix.is_empty());
902    /// ```
903    #[must_use]
904    pub fn match_slices<'d>(
905        &self,
906        data: &'d [u8],
907    ) -> Option<(&'d [u8], &'d [u8], &'d [u8], &'d [u8], &'d [u8], &'d [u8])> {
908        let [r0, r1, r2, r3, r4, r5] = (self.f)(data)?;
909        Some((
910            &data[r0], &data[r1], &data[r2], &data[r3], &data[r4], &data[r5],
911        ))
912    }
913}
914impl<F: Fn(&[u8]) -> Option<[Range<usize>; 6]>> IsMatch for Matcher6<F> {
915    fn is_match(&self, data: &[u8]) -> bool {
916        self.is_match(data)
917    }
918}
919
920/// A compiled regular expression with 7 capturing groups.
921///
922/// This is a zero-length type.
923/// The `regex!` macro generates a Rust type that implements the regular expression.
924/// This struct holds that type.
925pub struct Matcher7<F>
926where
927    F: Fn(&[u8]) -> Option<[Range<usize>; 7]>,
928{
929    f: F,
930}
931impl<F> Matcher7<F>
932where
933    F: Fn(&[u8]) -> Option<[Range<usize>; 7]>,
934{
935    /// This is used internally by the `regex!` macro.
936    #[must_use]
937    pub fn new(f: F) -> Self {
938        Self { f }
939    }
940
941    /// Returns `true` if `data` matches the regular expression,
942    /// otherwise returns `false`.
943    ///
944    /// This is a whole-string match.
945    /// For sub-string search, put `.*` at the beginning and end of the regex.
946    ///
947    /// # Example
948    /// ```rust
949    /// use safe_regex::{regex, Matcher0};
950    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
951    /// assert!(matcher.is_match(b"a42"));
952    /// assert!(!matcher.is_match(b"X"));
953    /// ```
954    #[must_use]
955    pub fn is_match(&self, data: &[u8]) -> bool {
956        (self.f)(data).is_some()
957    }
958
959    /// Executes the regular expression against the byte slice `data`.
960    ///
961    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
962    /// The tuple fields are ranges of bytes in `data` that matched capturing
963    /// groups in the expression.
964    /// A capturing group that matches no bytes will produce as a zero-length
965    /// range.
966    ///
967    /// This is a whole-string match.
968    /// For sub-string search, put `.*` at the beginning and end of the regex.
969    ///
970    /// Returns `None` if the expression did not match `data`.
971    ///
972    /// # Example
973    /// ```rust
974    /// use safe_regex::{regex, Matcher3};
975    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
976    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
977    /// assert_eq!(0..1_usize, prefix);
978    /// assert_eq!(1..3_usize, digits);
979    /// assert_eq!(0..0_usize, suffix);
980    /// ```
981    #[must_use]
982    pub fn match_ranges(
983        &self,
984        data: &[u8],
985    ) -> Option<(
986        Range<usize>,
987        Range<usize>,
988        Range<usize>,
989        Range<usize>,
990        Range<usize>,
991        Range<usize>,
992        Range<usize>,
993    )> {
994        let [r0, r1, r2, r3, r4, r5, r6] = (self.f)(data)?;
995        Some((r0, r1, r2, r3, r4, r5, r6))
996    }
997
998    /// Executes the regular expression against the byte slice `data`.
999    ///
1000    /// Returns `Some((&[u8],&[u8],...))`
1001    /// if the expression matched all of the bytes in `data`.
1002    /// The tuple fields are slices of `data` that matched
1003    /// capturing groups in the expression.
1004    ///
1005    /// This is a whole-string match.
1006    /// For sub-string search, put `.*` at the beginning and end of the regex.
1007    ///
1008    /// Returns `None` if the expression did not match `data`.
1009    ///
1010    /// # Example
1011    /// ```rust
1012    /// use safe_regex::{regex, Matcher3};
1013    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1014    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
1015    /// assert_eq!(b"a", prefix);
1016    /// assert_eq!(b"42", digits);
1017    /// assert!(suffix.is_empty());
1018    /// ```
1019    #[must_use]
1020    pub fn match_slices<'d>(
1021        &self,
1022        data: &'d [u8],
1023    ) -> Option<(
1024        &'d [u8],
1025        &'d [u8],
1026        &'d [u8],
1027        &'d [u8],
1028        &'d [u8],
1029        &'d [u8],
1030        &'d [u8],
1031    )> {
1032        let [r0, r1, r2, r3, r4, r5, r6] = (self.f)(data)?;
1033        Some((
1034            &data[r0], &data[r1], &data[r2], &data[r3], &data[r4], &data[r5], &data[r6],
1035        ))
1036    }
1037}
1038impl<F: Fn(&[u8]) -> Option<[Range<usize>; 7]>> IsMatch for Matcher7<F> {
1039    fn is_match(&self, data: &[u8]) -> bool {
1040        self.is_match(data)
1041    }
1042}
1043
1044/// A compiled regular expression with 8 capturing groups.
1045///
1046/// This is a zero-length type.
1047/// The `regex!` macro generates a Rust type that implements the regular expression.
1048/// This struct holds that type.
1049pub struct Matcher8<F>
1050where
1051    F: Fn(&[u8]) -> Option<[Range<usize>; 8]>,
1052{
1053    f: F,
1054}
1055impl<F> Matcher8<F>
1056where
1057    F: Fn(&[u8]) -> Option<[Range<usize>; 8]>,
1058{
1059    /// This is used internally by the `regex!` macro.
1060    #[must_use]
1061    pub fn new(f: F) -> Self {
1062        Self { f }
1063    }
1064
1065    /// Returns `true` if `data` matches the regular expression,
1066    /// otherwise returns `false`.
1067    ///
1068    /// This is a whole-string match.
1069    /// For sub-string search, put `.*` at the beginning and end of the regex.
1070    ///
1071    /// # Example
1072    /// ```rust
1073    /// use safe_regex::{regex, Matcher0};
1074    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
1075    /// assert!(matcher.is_match(b"a42"));
1076    /// assert!(!matcher.is_match(b"X"));
1077    /// ```
1078    #[must_use]
1079    pub fn is_match(&self, data: &[u8]) -> bool {
1080        (self.f)(data).is_some()
1081    }
1082
1083    /// Executes the regular expression against the byte slice `data`.
1084    ///
1085    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
1086    /// The tuple fields are ranges of bytes in `data` that matched capturing
1087    /// groups in the expression.
1088    /// A capturing group that matches no bytes will produce as a zero-length
1089    /// range.
1090    ///
1091    /// This is a whole-string match.
1092    /// For sub-string search, put `.*` at the beginning and end of the regex.
1093    ///
1094    /// Returns `None` if the expression did not match `data`.
1095    ///
1096    /// # Example
1097    /// ```rust
1098    /// use safe_regex::{regex, Matcher3};
1099    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1100    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
1101    /// assert_eq!(0..1_usize, prefix);
1102    /// assert_eq!(1..3_usize, digits);
1103    /// assert_eq!(0..0_usize, suffix);
1104    /// ```
1105    #[must_use]
1106    pub fn match_ranges(
1107        &self,
1108        data: &[u8],
1109    ) -> Option<(
1110        Range<usize>,
1111        Range<usize>,
1112        Range<usize>,
1113        Range<usize>,
1114        Range<usize>,
1115        Range<usize>,
1116        Range<usize>,
1117        Range<usize>,
1118    )> {
1119        let [r0, r1, r2, r3, r4, r5, r6, r7] = (self.f)(data)?;
1120        Some((r0, r1, r2, r3, r4, r5, r6, r7))
1121    }
1122
1123    /// Executes the regular expression against the byte slice `data`.
1124    ///
1125    /// Returns `Some((&[u8],&[u8],...))`
1126    /// if the expression matched all of the bytes in `data`.
1127    /// The tuple fields are slices of `data` that matched
1128    /// capturing groups in the expression.
1129    ///
1130    /// This is a whole-string match.
1131    /// For sub-string search, put `.*` at the beginning and end of the regex.
1132    ///
1133    /// Returns `None` if the expression did not match `data`.
1134    ///
1135    /// # Example
1136    /// ```rust
1137    /// use safe_regex::{regex, Matcher3};
1138    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1139    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
1140    /// assert_eq!(b"a", prefix);
1141    /// assert_eq!(b"42", digits);
1142    /// assert!(suffix.is_empty());
1143    /// ```
1144    #[must_use]
1145    pub fn match_slices<'d>(
1146        &self,
1147        data: &'d [u8],
1148    ) -> Option<(
1149        &'d [u8],
1150        &'d [u8],
1151        &'d [u8],
1152        &'d [u8],
1153        &'d [u8],
1154        &'d [u8],
1155        &'d [u8],
1156        &'d [u8],
1157    )> {
1158        let [r0, r1, r2, r3, r4, r5, r6, r7] = (self.f)(data)?;
1159        Some((
1160            &data[r0], &data[r1], &data[r2], &data[r3], &data[r4], &data[r5], &data[r6], &data[r7],
1161        ))
1162    }
1163}
1164impl<F: Fn(&[u8]) -> Option<[Range<usize>; 8]>> IsMatch for Matcher8<F> {
1165    fn is_match(&self, data: &[u8]) -> bool {
1166        self.is_match(data)
1167    }
1168}
1169
1170/// A compiled regular expression with 9 capturing groups.
1171///
1172/// This is a zero-length type.
1173/// The `regex!` macro generates a Rust type that implements the regular expression.
1174/// This struct holds that type.
1175pub struct Matcher9<F>
1176where
1177    F: Fn(&[u8]) -> Option<[Range<usize>; 9]>,
1178{
1179    f: F,
1180}
1181impl<F> Matcher9<F>
1182where
1183    F: Fn(&[u8]) -> Option<[Range<usize>; 9]>,
1184{
1185    /// This is used internally by the `regex!` macro.
1186    #[must_use]
1187    pub fn new(f: F) -> Self {
1188        Self { f }
1189    }
1190
1191    /// Returns `true` if `data` matches the regular expression,
1192    /// otherwise returns `false`.
1193    ///
1194    /// This is a whole-string match.
1195    /// For sub-string search, put `.*` at the beginning and end of the regex.
1196    ///
1197    /// # Example
1198    /// ```rust
1199    /// use safe_regex::{regex, Matcher0};
1200    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
1201    /// assert!(matcher.is_match(b"a42"));
1202    /// assert!(!matcher.is_match(b"X"));
1203    /// ```
1204    #[must_use]
1205    pub fn is_match(&self, data: &[u8]) -> bool {
1206        (self.f)(data).is_some()
1207    }
1208
1209    /// Executes the regular expression against the byte slice `data`.
1210    ///
1211    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
1212    /// The tuple fields are ranges of bytes in `data` that matched capturing
1213    /// groups in the expression.
1214    /// A capturing group that matches no bytes will produce as a zero-length
1215    /// range.
1216    ///
1217    /// This is a whole-string match.
1218    /// For sub-string search, put `.*` at the beginning and end of the regex.
1219    ///
1220    /// Returns `None` if the expression did not match `data`.
1221    ///
1222    /// # Example
1223    /// ```rust
1224    /// use safe_regex::{regex, Matcher3};
1225    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1226    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
1227    /// assert_eq!(0..1_usize, prefix);
1228    /// assert_eq!(1..3_usize, digits);
1229    /// assert_eq!(0..0_usize, suffix);
1230    /// ```
1231    #[must_use]
1232    pub fn match_ranges(
1233        &self,
1234        data: &[u8],
1235    ) -> Option<(
1236        Range<usize>,
1237        Range<usize>,
1238        Range<usize>,
1239        Range<usize>,
1240        Range<usize>,
1241        Range<usize>,
1242        Range<usize>,
1243        Range<usize>,
1244        Range<usize>,
1245    )> {
1246        let [r0, r1, r2, r3, r4, r5, r6, r7, r8] = (self.f)(data)?;
1247        Some((r0, r1, r2, r3, r4, r5, r6, r7, r8))
1248    }
1249
1250    /// Executes the regular expression against the byte slice `data`.
1251    ///
1252    /// Returns `Some((&[u8],&[u8],...))`
1253    /// if the expression matched all of the bytes in `data`.
1254    /// The tuple fields are slices of `data` that matched
1255    /// capturing groups in the expression.
1256    ///
1257    /// This is a whole-string match.
1258    /// For sub-string search, put `.*` at the beginning and end of the regex.
1259    ///
1260    /// Returns `None` if the expression did not match `data`.
1261    ///
1262    /// # Example
1263    /// ```rust
1264    /// use safe_regex::{regex, Matcher3};
1265    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1266    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
1267    /// assert_eq!(b"a", prefix);
1268    /// assert_eq!(b"42", digits);
1269    /// assert!(suffix.is_empty());
1270    /// ```
1271    #[must_use]
1272    pub fn match_slices<'d>(
1273        &self,
1274        data: &'d [u8],
1275    ) -> Option<(
1276        &'d [u8],
1277        &'d [u8],
1278        &'d [u8],
1279        &'d [u8],
1280        &'d [u8],
1281        &'d [u8],
1282        &'d [u8],
1283        &'d [u8],
1284        &'d [u8],
1285    )> {
1286        let [r0, r1, r2, r3, r4, r5, r6, r7, r8] = (self.f)(data)?;
1287        Some((
1288            &data[r0], &data[r1], &data[r2], &data[r3], &data[r4], &data[r5], &data[r6], &data[r7],
1289            &data[r8],
1290        ))
1291    }
1292}
1293impl<F: Fn(&[u8]) -> Option<[Range<usize>; 9]>> IsMatch for Matcher9<F> {
1294    fn is_match(&self, data: &[u8]) -> bool {
1295        self.is_match(data)
1296    }
1297}
1298
1299/// A compiled regular expression with 10 capturing groups.
1300///
1301/// This is a zero-length type.
1302/// The `regex!` macro generates a Rust type that implements the regular expression.
1303/// This struct holds that type.
1304pub struct Matcher10<F>
1305where
1306    F: Fn(&[u8]) -> Option<[Range<usize>; 10]>,
1307{
1308    f: F,
1309}
1310impl<F> Matcher10<F>
1311where
1312    F: Fn(&[u8]) -> Option<[Range<usize>; 10]>,
1313{
1314    /// This is used internally by the `regex!` macro.
1315    #[must_use]
1316    pub fn new(f: F) -> Self {
1317        Self { f }
1318    }
1319
1320    /// Returns `true` if `data` matches the regular expression,
1321    /// otherwise returns `false`.
1322    ///
1323    /// This is a whole-string match.
1324    /// For sub-string search, put `.*` at the beginning and end of the regex.
1325    ///
1326    /// # Example
1327    /// ```rust
1328    /// use safe_regex::{regex, Matcher0};
1329    /// let matcher: Matcher0<_> = regex!(br"[abc][0-9]*");
1330    /// assert!(matcher.is_match(b"a42"));
1331    /// assert!(!matcher.is_match(b"X"));
1332    /// ```
1333    #[must_use]
1334    pub fn is_match(&self, data: &[u8]) -> bool {
1335        (self.f)(data).is_some()
1336    }
1337
1338    /// Executes the regular expression against the byte slice `data`.
1339    ///
1340    /// Returns `Some((Range<u32>,Range<u32>,...))` if the expression matched all of the bytes in `data`.
1341    /// The tuple fields are ranges of bytes in `data` that matched capturing
1342    /// groups in the expression.
1343    /// A capturing group that matches no bytes will produce as a zero-length
1344    /// range.
1345    ///
1346    /// This is a whole-string match.
1347    /// For sub-string search, put `.*` at the beginning and end of the regex.
1348    ///
1349    /// Returns `None` if the expression did not match `data`.
1350    ///
1351    /// # Example
1352    /// ```rust
1353    /// use safe_regex::{regex, Matcher3};
1354    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1355    /// let (prefix, digits, suffix) = matcher.match_ranges(b"a42").unwrap();
1356    /// assert_eq!(0..1_usize, prefix);
1357    /// assert_eq!(1..3_usize, digits);
1358    /// assert_eq!(0..0_usize, suffix);
1359    /// ```
1360    #[must_use]
1361    pub fn match_ranges(
1362        &self,
1363        data: &[u8],
1364    ) -> Option<(
1365        Range<usize>,
1366        Range<usize>,
1367        Range<usize>,
1368        Range<usize>,
1369        Range<usize>,
1370        Range<usize>,
1371        Range<usize>,
1372        Range<usize>,
1373        Range<usize>,
1374        Range<usize>,
1375    )> {
1376        let [r0, r1, r2, r3, r4, r5, r6, r7, r8, r9] = (self.f)(data)?;
1377        Some((r0, r1, r2, r3, r4, r5, r6, r7, r8, r9))
1378    }
1379
1380    /// Executes the regular expression against the byte slice `data`.
1381    ///
1382    /// Returns `Some((&[u8],&[u8],...))`
1383    /// if the expression matched all of the bytes in `data`.
1384    /// The tuple fields are slices of `data` that matched
1385    /// capturing groups in the expression.
1386    ///
1387    /// This is a whole-string match.
1388    /// For sub-string search, put `.*` at the beginning and end of the regex.
1389    ///
1390    /// Returns `None` if the expression did not match `data`.
1391    ///
1392    /// # Example
1393    /// ```rust
1394    /// use safe_regex::{regex, Matcher3};
1395    /// let matcher: Matcher3<_> = regex!(br"([abc])([0-9]*)(suffix)?");
1396    /// let (prefix, digits, suffix) = matcher.match_slices(b"a42").unwrap();
1397    /// assert_eq!(b"a", prefix);
1398    /// assert_eq!(b"42", digits);
1399    /// assert!(suffix.is_empty());
1400    /// ```
1401    #[must_use]
1402    pub fn match_slices<'d>(
1403        &self,
1404        data: &'d [u8],
1405    ) -> Option<(
1406        &'d [u8],
1407        &'d [u8],
1408        &'d [u8],
1409        &'d [u8],
1410        &'d [u8],
1411        &'d [u8],
1412        &'d [u8],
1413        &'d [u8],
1414        &'d [u8],
1415        &'d [u8],
1416    )> {
1417        let [r0, r1, r2, r3, r4, r5, r6, r7, r8, r9] = (self.f)(data)?;
1418        Some((
1419            &data[r0], &data[r1], &data[r2], &data[r3], &data[r4], &data[r5], &data[r6], &data[r7],
1420            &data[r8], &data[r9],
1421        ))
1422    }
1423}
1424impl<F: Fn(&[u8]) -> Option<[Range<usize>; 10]>> IsMatch for Matcher10<F> {
1425    fn is_match(&self, data: &[u8]) -> bool {
1426        self.is_match(data)
1427    }
1428}