safe_regex/lib.rs
1//! [](https://crates.io/crates/safe-regex)
2//! [](http://www.apache.org/licenses/LICENSE-2.0)
3//! [](https://github.com/rust-secure-code/safety-dance/)
4//! [](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}