1use std::collections::HashMap;
15use std::marker::PhantomData;
16use std::ops::Index;
17use std::os::raw::c_char;
18use std::sync::Arc;
19
20pub const VERSION: &str = env!("CARGO_PKG_VERSION");
22
23enum RealRegex {}
25enum RealIter {}
26enum RealRegexSet {}
27
28extern "C" {
29 fn real_compile(pattern: *const c_char, len: usize, flags: u32,
30 errbuf: *mut c_char, errbuf_len: usize, code: *mut i32) -> *mut RealRegex;
31 fn real_group_count(re: *const RealRegex) -> usize;
32 fn real_group_name(re: *const RealRegex, group: usize, buf: *mut c_char, buflen: usize) -> usize;
33 fn real_free(re: *mut RealRegex);
34 fn real_find_iter(re: *const RealRegex, text: *const c_char, len: usize) -> *mut RealIter;
35 fn real_find_iter_at(re: *const RealRegex, text: *const c_char, len: usize, start: usize) -> *mut RealIter;
36 fn real_iter_next(iter: *mut RealIter, spans: *mut usize) -> i32;
37 fn real_iter_free(iter: *mut RealIter);
38 fn real_count_matches(re: *const RealRegex, text: *const c_char, len: usize) -> usize;
39 fn real_set_compile(patterns: *const *const c_char, lens: *const usize, n: usize, flags: u32,
40 errbuf: *mut c_char, errbuf_len: usize, code: *mut i32) -> *mut RealRegexSet;
41 fn real_set_size(set: *const RealRegexSet) -> usize;
42 fn real_set_free(set: *mut RealRegexSet);
43 fn real_set_is_match(set: *const RealRegexSet, text: *const c_char, len: usize) -> i32;
44 fn real_set_matches(set: *const RealRegexSet, text: *const c_char, len: usize, out: *mut u8) -> i32;
45}
46
47const DIVERGENCES_URL: &str = "https://github.com/RECHE23/real-regex/blob/main/docs/COMPATIBILITY.md";
48const REAL_ERR_UNSUPPORTED: i32 = 2; const DOLLAR_ENDONLY: u32 = 128;
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum Error {
56 Syntax { msg: String, pos: Option<usize> },
58 Unsupported { construct: String, hint: String },
61}
62
63impl Error {
64 pub fn is_unsupported(&self) -> bool {
66 matches!(self, Error::Unsupported { .. })
67 }
68
69 fn from_engine(raw: &str, code: i32) -> Error {
73 let body = raw.strip_prefix("regex_error").unwrap_or(raw).trim_start();
74 let (pos, msg) = match body.strip_prefix("at ").and_then(|r| r.split_once(':')) {
75 Some((n, rest)) => (n.trim().parse::<usize>().ok(), rest.trim().to_string()),
76 None => (None, body.trim_start_matches(':').trim().to_string()),
77 };
78 if code == REAL_ERR_UNSUPPORTED {
79 unsupported_construct(&msg)
80 } else {
81 Error::Syntax { msg, pos }
82 }
83 }
84}
85
86impl std::fmt::Display for Error {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 Error::Syntax { msg, pos: Some(p) } => write!(f, "syntax error at {p}: {msg}"),
90 Error::Syntax { msg, pos: None } => write!(f, "syntax error: {msg}"),
91 Error::Unsupported { construct, hint } => write!(f, "{construct} ({hint})"),
92 }
93 }
94}
95
96impl std::error::Error for Error {}
97
98struct GroupInfo {
101 names: Vec<Option<String>>, by_name: HashMap<String, usize>, }
104
105const CAPS_INLINE_SLOTS: usize = 8;
108
109#[derive(Clone, Debug)]
120enum SlotStore {
121 Inline { len: u8, slots: [usize; CAPS_INLINE_SLOTS] },
122 Spilled(Box<[usize]>),
123}
124
125impl SlotStore {
126 fn from_flat(src: &[usize]) -> SlotStore {
128 if src.len() == 2 {
136 let mut slots = [usize::MAX; CAPS_INLINE_SLOTS];
137 slots[0] = src[0];
138 slots[1] = src[1];
139 return SlotStore::Inline { len: 2, slots };
140 }
141 if src.len() <= CAPS_INLINE_SLOTS {
142 let mut slots = [usize::MAX; CAPS_INLINE_SLOTS];
143 slots[..src.len()].copy_from_slice(src);
144 SlotStore::Inline { len: src.len() as u8, slots }
145 } else {
146 SlotStore::Spilled(src.to_vec().into_boxed_slice())
147 }
148 }
149
150 fn as_slice(&self) -> &[usize] {
151 match self {
152 SlotStore::Inline { len, slots } => &slots[..*len as usize],
153 SlotStore::Spilled(b) => b,
154 }
155 }
156
157 fn group(&self, i: usize) -> Option<(usize, usize)> {
164 let s = self.as_slice();
165 let lo = i.checked_mul(2)?;
166 let a = *s.get(lo)?;
167 let b = *s.get(lo + 1)?;
168 if a == usize::MAX {
169 None
170 } else {
171 Some((a, b))
172 }
173 }
174
175 fn ngroups(&self) -> usize {
177 self.as_slice().len() / 2
178 }
179}
180
181fn unsupported_construct(construct: &str) -> Error {
183 Error::Unsupported {
184 construct: construct.to_string(),
185 hint: format!(
186 "unsupported by REAL — see {DIVERGENCES_URL} ; enable the `fallback` feature to delegate this \
187 pattern to the regex crate (forfeiting the linear-time guarantee for it)"
188 ),
189 }
190}
191
192fn nested_class_syntax(pattern: &[u8]) -> Option<&'static str> {
199 let mut i = 0;
200 let mut in_class = false;
201 let mut class_pos = 0usize; while i < pattern.len() {
203 let b = pattern[i];
204 if b == b'\\' {
205 i += 2; if in_class {
207 class_pos += 1;
208 }
209 continue;
210 }
211 if !in_class {
212 if b == b'[' {
213 in_class = true;
214 class_pos = 0;
215 if pattern.get(i + 1) == Some(&b'^') {
216 i += 1; }
218 }
219 } else if b == b']' {
220 if class_pos == 0 {
221 class_pos += 1; } else {
223 in_class = false;
224 }
225 } else if b == b'[' {
226 return Some("nested character class");
227 } else if matches!(b, b'&' | b'-' | b'~') && pattern.get(i + 1) == Some(&b) {
228 return Some("character-class set operation");
229 } else {
230 class_pos += 1;
231 }
232 i += 1;
233 }
234 None
235}
236
237fn compile_handle(pattern: &[u8], flags: u32) -> Result<(*mut RealRegex, usize, Arc<GroupInfo>), Error> {
239 if let Some(construct) = nested_class_syntax(pattern) {
240 return Err(unsupported_construct(construct)); }
242 let mut err = [0u8; 256];
243 let mut code: i32 = 0;
244 let handle = unsafe {
245 real_compile(pattern.as_ptr() as *const c_char, pattern.len(), flags | DOLLAR_ENDONLY,
246 err.as_mut_ptr() as *mut c_char, err.len(), &mut code)
247 };
248 if handle.is_null() {
249 let end = err.iter().position(|&b| b == 0).unwrap_or(err.len());
250 return Err(Error::from_engine(&String::from_utf8_lossy(&err[..end]), code));
251 }
252 let ngroups = unsafe { real_group_count(handle) };
253 let mut names = Vec::with_capacity(ngroups);
254 let mut by_name = HashMap::new();
255 for g in 0..ngroups {
258 let len = unsafe { real_group_name(handle, g, std::ptr::null_mut(), 0) };
259 if len == 0 {
260 names.push(None);
261 } else {
262 let mut buf = vec![0u8; len + 1];
263 unsafe {
264 real_group_name(handle, g, buf.as_mut_ptr() as *mut c_char, buf.len());
265 }
266 let name = String::from_utf8_lossy(&buf[..len]).into_owned();
267 by_name.insert(name.clone(), g);
268 names.push(Some(name));
269 }
270 }
271 Ok((handle, ngroups, Arc::new(GroupInfo { names, by_name })))
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
276pub enum Engine {
277 Real,
279 Fallback,
281}
282
283pub struct Regex {
285 handle: *mut RealRegex, ngroups: usize, pattern: String,
288 groups: Arc<GroupInfo>,
289 #[cfg(feature = "fallback")]
290 fallback: Option<regex::Regex>, }
292
293unsafe impl Send for Regex {}
296unsafe impl Sync for Regex {}
297
298impl Regex {
299 pub fn new(pattern: &str) -> Result<Regex, Error> {
302 Regex::with_flags(pattern, 0)
303 }
304
305 pub fn with_flags(pattern: &str, flags: u32) -> Result<Regex, Error> {
308 let (handle, ngroups, groups) = compile_handle(pattern.as_bytes(), flags)?;
309 Ok(Regex {
310 handle,
311 ngroups,
312 pattern: pattern.to_string(),
313 groups,
314 #[cfg(feature = "fallback")]
315 fallback: None,
316 })
317 }
318
319 pub fn engine(&self) -> Engine {
322 #[cfg(feature = "fallback")]
323 if self.fallback.is_some() {
324 return Engine::Fallback;
325 }
326 Engine::Real
327 }
328
329 #[cfg(feature = "fallback")]
332 fn build_fallback(pattern: &str, flags: u32) -> Result<Regex, Error> {
333 let fb = regex::RegexBuilder::new(pattern)
334 .case_insensitive(flags & FLAG_ICASE != 0)
335 .multi_line(flags & FLAG_MULTILINE != 0)
336 .dot_matches_new_line(flags & FLAG_DOTALL != 0)
337 .ignore_whitespace(flags & FLAG_VERBOSE != 0)
338 .unicode(flags & FLAG_ASCII == 0)
339 .build()
340 .map_err(|e| Error::Syntax { msg: e.to_string(), pos: None })?;
341 let ngroups = fb.captures_len();
342 let mut names = Vec::with_capacity(ngroups);
343 let mut by_name = HashMap::new();
344 for (i, n) in fb.capture_names().enumerate() {
345 match n {
346 Some(name) => {
347 by_name.insert(name.to_string(), i);
348 names.push(Some(name.to_string()));
349 }
350 None => names.push(None),
351 }
352 }
353 Ok(Regex {
354 handle: std::ptr::null_mut(),
355 ngroups,
356 pattern: pattern.to_string(),
357 groups: Arc::new(GroupInfo { names, by_name }),
358 fallback: Some(fb),
359 })
360 }
361
362 pub fn as_str(&self) -> &str {
364 &self.pattern
365 }
366
367 pub fn captures_len(&self) -> usize {
370 self.ngroups
371 }
372
373 pub fn capture_names(&self) -> impl Iterator<Item = Option<&str>> {
375 self.groups.names.iter().map(|o| o.as_deref())
376 }
377
378 fn raw<'r, 't>(&'r self, text: &'t str, start: Option<usize>) -> SpanCursor<'r, 't> {
379 #[cfg(feature = "fallback")]
380 if let Some(fb) = &self.fallback {
381 return SpanCursor::Fallback {
382 it: fb.captures_iter(text),
383 ngroups: self.ngroups,
384 min_start: start.unwrap_or(0),
385 cur: Vec::new(),
386 };
387 }
388 let iter = unsafe {
389 match start {
390 None => real_find_iter(self.handle, text.as_ptr() as *const c_char, text.len()),
391 Some(s) => real_find_iter_at(self.handle, text.as_ptr() as *const c_char, text.len(), s),
392 }
393 };
394 assert!(!iter.is_null(), "real-regex: engine iteration failed");
396 SpanCursor::Real(RawSpans { iter, handle: self.handle, text: text.as_bytes(), ngroups: self.ngroups, buf: vec![0usize; 2 * self.ngroups], last_end: None, drive_pos: None, utf8: true, _re: PhantomData })
397 }
398
399 fn caps_from<'t>(&self, text: &'t str, cur: &SpanCursor<'_, '_>) -> Captures<'t> {
400 Captures { text, slots: cur.slot_store(), groups: Arc::clone(&self.groups) }
401 }
402
403 pub fn is_match(&self, text: &str) -> bool {
405 self.raw(text, None).advance().is_some()
406 }
407
408 pub fn is_match_at(&self, text: &str, start: usize) -> bool {
410 self.raw(text, Some(start)).advance().is_some()
411 }
412
413 pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>> {
415 self.raw(text, None).advance().map(|(a, b)| Match { text, start: a, end: b })
416 }
417
418 pub fn find_at<'t>(&self, text: &'t str, start: usize) -> Option<Match<'t>> {
420 self.raw(text, Some(start)).advance().map(|(a, b)| Match { text, start: a, end: b })
421 }
422
423 pub fn find_iter<'r, 't>(&'r self, text: &'t str) -> Matches<'r, 't> {
425 Matches { raw: self.raw(text, None), text }
426 }
427
428 pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
430 {
431 let mut c = self.raw(text, None);
432 c.advance().map(|_| self.caps_from(text, &c))
433 }
434 }
435
436 pub fn captures_at<'t>(&self, text: &'t str, start: usize) -> Option<Captures<'t>> {
438 {
439 let mut c = self.raw(text, Some(start));
440 c.advance().map(|_| self.caps_from(text, &c))
441 }
442 }
443
444 pub fn capture_locations(&self) -> CaptureLocations {
448 CaptureLocations {
449 slots: vec![0; 2 * self.ngroups],
450 ngroups: self.ngroups,
451 }
452 }
453
454 pub fn captures_read<'t>(
457 &self,
458 locs: &mut CaptureLocations,
459 text: &'t str,
460 ) -> Option<Match<'t>> {
461 self.captures_read_at(locs, text, 0)
462 }
463
464 pub fn captures_read_at<'t>(
466 &self,
467 locs: &mut CaptureLocations,
468 text: &'t str,
469 start: usize,
470 ) -> Option<Match<'t>> {
471 locs.ensure(self.ngroups);
472 let mut c = self.raw(text, if start == 0 { None } else { Some(start) });
473 let (a, b) = c.advance()?;
474 c.copy_slots_into(locs);
475 Some(Match {
476 text,
477 start: a,
478 end: b,
479 })
480 }
481
482 pub fn captures_read_iter<'r, 't>(
488 &'r self,
489 text: &'t str,
490 ) -> CaptureLocationMatches<'r, 't> {
491 CaptureLocationMatches {
492 raw: self.raw(text, None),
493 text,
494 ngroups: self.ngroups,
495 }
496 }
497
498 pub fn captures_iter<'r, 't>(&'r self, text: &'t str) -> CaptureMatches<'r, 't> {
500 CaptureMatches { raw: self.raw(text, None), re: self, text }
501 }
502
503 pub fn shortest_match(&self, text: &str) -> Option<usize> {
509 #[cfg(feature = "fallback")]
510 if let Some(fb) = &self.fallback {
511 return fb.shortest_match(text); }
513 self.raw(text, None).advance().map(|(_, e)| e)
514 }
515
516 pub fn count_matches(&self, text: &str) -> usize {
522 #[cfg(feature = "fallback")]
523 if let Some(fb) = &self.fallback {
524 return fb.find_iter(text).count();
525 }
526 let n = unsafe {
527 real_count_matches(self.handle, text.as_ptr() as *const c_char, text.len())
528 };
529 assert_ne!(n, usize::MAX, "real-regex: count_matches failed");
530 n
531 }
532}
533
534pub struct RegexSet {
541 handle: *mut RealRegexSet,
542 patterns: Vec<String>,
543}
544
545unsafe impl Send for RegexSet {}
546unsafe impl Sync for RegexSet {}
547
548impl RegexSet {
549 pub fn new<I, S>(patterns: I) -> Result<RegexSet, Error>
551 where
552 I: IntoIterator<Item = S>,
553 S: AsRef<str>,
554 {
555 RegexSet::with_flags(patterns, 0)
556 }
557
558 pub fn with_flags<I, S>(patterns: I, flags: u32) -> Result<RegexSet, Error>
560 where
561 I: IntoIterator<Item = S>,
562 S: AsRef<str>,
563 {
564 let owned: Vec<String> = patterns.into_iter().map(|s| s.as_ref().to_string()).collect();
565 let mut ptrs: Vec<*const c_char> = Vec::with_capacity(owned.len());
566 let mut lens: Vec<usize> = Vec::with_capacity(owned.len());
567 for p in &owned {
568 ptrs.push(p.as_ptr() as *const c_char);
569 lens.push(p.len());
570 }
571 let mut err = [0i8; 512];
572 let mut code: i32 = 0;
573 let handle = unsafe {
574 real_set_compile(
575 ptrs.as_ptr(),
576 lens.as_ptr(),
577 owned.len(),
578 flags | DOLLAR_ENDONLY,
579 err.as_mut_ptr(),
580 err.len(),
581 &mut code,
582 )
583 };
584 if handle.is_null() {
585 let raw = unsafe { std::ffi::CStr::from_ptr(err.as_ptr()) }
586 .to_string_lossy()
587 .into_owned();
588 return Err(Error::from_engine(&raw, code));
589 }
590 Ok(RegexSet {
591 handle,
592 patterns: owned,
593 })
594 }
595
596 pub fn len(&self) -> usize {
598 unsafe { real_set_size(self.handle) }
599 }
600
601 pub fn is_empty(&self) -> bool {
603 self.len() == 0
604 }
605
606 pub fn patterns(&self) -> &[String] {
608 &self.patterns
609 }
610
611 pub fn is_match(&self, text: &str) -> bool {
613 let r = unsafe {
614 real_set_is_match(self.handle, text.as_ptr() as *const c_char, text.len())
615 };
616 r == 1
617 }
618
619 pub fn matches(&self, text: &str) -> Vec<bool> {
622 let n = self.len();
623 let mut out = vec![0u8; n];
624 let r = unsafe {
625 real_set_matches(
626 self.handle,
627 text.as_ptr() as *const c_char,
628 text.len(),
629 out.as_mut_ptr(),
630 )
631 };
632 assert_eq!(r, 0, "real-regex: regex_set matches failed");
633 out.into_iter().map(|b| b != 0).collect()
634 }
635
636 pub fn matched_ids(&self, text: &str) -> Vec<usize> {
638 self.matches(text)
639 .into_iter()
640 .enumerate()
641 .filter_map(|(i, hit)| hit.then_some(i))
642 .collect()
643 }
644}
645
646impl Drop for RegexSet {
647 fn drop(&mut self) {
648 if !self.handle.is_null() {
649 unsafe { real_set_free(self.handle) }
650 }
651 }
652}
653
654impl Drop for Regex {
655 fn drop(&mut self) {
656 if !self.handle.is_null() {
657 unsafe { real_free(self.handle) } }
659 }
660}
661
662impl std::fmt::Debug for Regex {
663 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664 write!(f, "Regex({:?})", self.pattern)
665 }
666}
667
668struct RawSpans<'r, 't> {
670 iter: *mut RealIter, handle: *const RealRegex, text: &'t [u8], ngroups: usize,
674 buf: Vec<usize>, last_end: Option<usize>, drive_pos: Option<usize>, utf8: bool, _re: PhantomData<&'r ()>, }
680
681impl RawSpans<'_, '_> {
682 fn advance(&mut self) -> Option<(usize, usize)> {
694 if self.drive_pos.is_some() {
695 return self.drive_advance();
696 }
697 loop {
698 let got = unsafe { real_iter_next(self.iter, self.buf.as_mut_ptr()) };
699 match got {
700 0 => return None,
701 -1 => panic!("real-regex: engine iteration failed"),
704 _ => {
705 let (s0, e0) = (self.buf[0], self.buf[1]); if s0 == e0 {
707 self.drive_pos = Some(self.last_end.unwrap_or(0));
710 return self.drive_advance();
711 }
712 self.last_end = Some(e0);
713 return Some((s0, e0));
714 }
715 }
716 }
717 }
718
719 fn search_at(&mut self, pos: usize) -> Option<(usize, usize)> {
722 if pos > self.text.len() {
723 return None;
724 }
725 let it = unsafe {
726 real_find_iter_at(self.handle, self.text.as_ptr() as *const c_char, self.text.len(), pos)
727 };
728 assert!(!it.is_null(), "real-regex: engine iteration failed");
729 let got = unsafe { real_iter_next(it, self.buf.as_mut_ptr()) };
730 unsafe { real_iter_free(it) };
731 match got {
732 0 => None,
733 -1 => panic!("real-regex: engine iteration failed"),
734 _ => Some((self.buf[0], self.buf[1])),
735 }
736 }
737
738 fn step_len(&self, pos: usize) -> usize {
741 if !self.utf8 || pos >= self.text.len() {
742 return 1;
743 }
744 match self.text[pos] {
745 b if b < 0x80 => 1,
746 b if b < 0xE0 => 2,
747 b if b < 0xF0 => 3,
748 _ => 4,
749 }
750 }
751
752 fn drive_advance(&mut self) -> Option<(usize, usize)> {
756 let pos = self.drive_pos.expect("drive_advance in fast mode");
757 let mut m = self.search_at(pos)?;
758 if m.0 == m.1 && Some(m.1) == self.last_end {
759 let next = m.1 + self.step_len(m.1);
760 m = self.search_at(next)?;
761 }
762 self.last_end = Some(m.1);
763 self.drive_pos = Some(m.1);
764 Some(m)
765 }
766
767}
768
769impl Drop for RawSpans<'_, '_> {
770 fn drop(&mut self) {
771 unsafe { real_iter_free(self.iter) }
772 }
773}
774
775enum SpanCursor<'r, 't> {
778 Real(RawSpans<'r, 't>),
779 #[cfg(feature = "fallback")]
780 Fallback {
781 it: regex::CaptureMatches<'r, 't>,
782 ngroups: usize,
783 min_start: usize,
784 cur: Vec<Option<(usize, usize)>>, },
786}
787
788impl SpanCursor<'_, '_> {
789 fn advance(&mut self) -> Option<(usize, usize)> {
793 match self {
794 SpanCursor::Real(r) => r.advance(),
795 #[cfg(feature = "fallback")]
796 SpanCursor::Fallback { it, ngroups, min_start, cur } => loop {
797 let caps = it.next()?;
798 let m0 = caps.get(0).unwrap();
799 if m0.start() < *min_start {
800 continue; }
802 cur.clear();
803 cur.extend((0..*ngroups).map(|g| caps.get(g).map(|m| (m.start(), m.end()))));
804 return Some((m0.start(), m0.end()));
805 },
806 }
807 }
808
809 fn nslots(&self) -> usize {
811 match self {
812 SpanCursor::Real(r) => 2 * r.ngroups,
813 #[cfg(feature = "fallback")]
814 SpanCursor::Fallback { ngroups, .. } => 2 * *ngroups,
815 }
816 }
817
818 fn write_slots(&self, out: &mut [usize]) {
822 match self {
823 SpanCursor::Real(r) => out.copy_from_slice(&r.buf),
824 #[cfg(feature = "fallback")]
825 SpanCursor::Fallback { cur, .. } => {
826 for (g, s) in cur.iter().enumerate() {
827 let (a, b) = s.unwrap_or((usize::MAX, usize::MAX));
828 out[2 * g] = a;
829 out[(2 * g) + 1] = b;
830 }
831 }
832 }
833 }
834
835 fn slot_store(&self) -> SlotStore {
837 match self {
838 SpanCursor::Real(r) => SlotStore::from_flat(&r.buf),
840 #[cfg(feature = "fallback")]
841 SpanCursor::Fallback { .. } => {
842 let n = self.nslots();
843 if n <= CAPS_INLINE_SLOTS {
844 let mut slots = [usize::MAX; CAPS_INLINE_SLOTS];
845 self.write_slots(&mut slots[..n]);
846 SlotStore::Inline { len: n as u8, slots }
847 } else {
848 let mut v = vec![usize::MAX; n];
849 self.write_slots(&mut v);
850 SlotStore::Spilled(v.into_boxed_slice())
851 }
852 }
853 }
854 }
855
856 fn copy_slots_into(&self, locs: &mut CaptureLocations) {
858 let ngroups = self.nslots() / 2;
859 locs.ensure(ngroups);
860 self.write_slots(&mut locs.slots);
861 }
862}
863
864#[derive(Clone, Debug)]
871pub struct CaptureLocations {
872 slots: Vec<usize>, ngroups: usize,
874}
875
876impl CaptureLocations {
877 pub fn len(&self) -> usize {
879 self.ngroups
880 }
881
882 pub fn is_empty(&self) -> bool {
884 self.ngroups == 0
885 }
886
887 pub fn get(&self, i: usize) -> Option<(usize, usize)> {
890 if i >= self.ngroups {
891 return None;
892 }
893 let a = self.slots[2 * i];
894 let b = self.slots[2 * i + 1];
895 if a == usize::MAX {
896 None
897 } else {
898 Some((a, b))
899 }
900 }
901
902 fn ensure(&mut self, ngroups: usize) {
903 if self.ngroups != ngroups || self.slots.len() != 2 * ngroups {
904 self.slots.resize(2 * ngroups, 0);
905 self.ngroups = ngroups;
906 }
907 }
908}
909
910#[derive(Clone, Copy, Debug, PartialEq, Eq)]
912pub struct Match<'t> {
913 text: &'t str,
914 start: usize,
915 end: usize,
916}
917
918impl<'t> Match<'t> {
919 pub fn start(&self) -> usize {
921 self.start
922 }
923
924 pub fn end(&self) -> usize {
926 self.end
927 }
928
929 pub fn range(&self) -> std::ops::Range<usize> {
931 self.start..self.end
932 }
933
934 pub fn as_str(&self) -> &'t str {
936 &self.text[self.start..self.end]
937 }
938
939 pub fn is_empty(&self) -> bool {
941 self.start == self.end
942 }
943
944 pub fn len(&self) -> usize {
946 self.end - self.start
947 }
948}
949
950pub struct Captures<'t> {
952 text: &'t str,
953 slots: SlotStore,
954 groups: Arc<GroupInfo>,
955}
956
957impl<'t> Captures<'t> {
958 pub fn get(&self, i: usize) -> Option<Match<'t>> {
960 self.slots.group(i).map(|(s, e)| Match { text: self.text, start: s, end: e })
961 }
962
963 pub fn name(&self, name: &str) -> Option<Match<'t>> {
965 self.groups.by_name.get(name).and_then(|&i| self.get(i))
966 }
967
968 pub fn len(&self) -> usize {
970 self.slots.ngroups()
971 }
972
973 pub fn is_empty(&self) -> bool {
975 self.slots.ngroups() == 0
976 }
977
978 pub fn iter(&self) -> impl Iterator<Item = Option<Match<'t>>> + '_ {
980 (0..self.len()).map(move |i| self.get(i))
981 }
982}
983
984impl Index<usize> for Captures<'_> {
986 type Output = str;
987 fn index(&self, i: usize) -> &str {
988 self.get(i).map(|m| m.as_str()).unwrap_or_else(|| panic!("no group at index {i}"))
989 }
990}
991
992impl Index<&str> for Captures<'_> {
993 type Output = str;
994 fn index(&self, name: &str) -> &str {
995 self.name(name).map(|m| m.as_str()).unwrap_or_else(|| panic!("no group named {name:?}"))
996 }
997}
998
999pub struct Matches<'r, 't> {
1001 raw: SpanCursor<'r, 't>,
1002 text: &'t str,
1003}
1004
1005impl<'t> Iterator for Matches<'_, 't> {
1006 type Item = Match<'t>;
1007 fn next(&mut self) -> Option<Match<'t>> {
1008 self.raw.advance().map(|(a, b)| Match { text: self.text, start: a, end: b })
1009 }
1010}
1011
1012pub struct CaptureMatches<'r, 't> {
1014 raw: SpanCursor<'r, 't>,
1015 re: &'r Regex,
1016 text: &'t str,
1017}
1018
1019pub struct CaptureLocationMatches<'r, 't> {
1024 raw: SpanCursor<'r, 't>,
1025 text: &'t str,
1026 ngroups: usize,
1027}
1028
1029impl CaptureLocationMatches<'_, '_> {
1030 pub fn len(&self) -> usize {
1032 self.ngroups
1033 }
1034
1035 pub fn is_empty(&self) -> bool {
1037 self.ngroups == 0
1038 }
1039
1040 pub fn get(&self, i: usize) -> Option<(usize, usize)> {
1043 if i >= self.ngroups {
1044 return None;
1045 }
1046 match &self.raw {
1047 SpanCursor::Real(r) => {
1048 let a = r.buf[2 * i];
1049 let b = r.buf[2 * i + 1];
1050 if a == usize::MAX {
1051 None
1052 } else {
1053 Some((a, b))
1054 }
1055 }
1056 #[cfg(feature = "fallback")]
1057 SpanCursor::Fallback { cur, .. } => cur.get(i).copied().flatten(),
1058 }
1059 }
1060
1061 pub fn read_captures(&self, locs: &mut CaptureLocations) {
1063 self.raw.copy_slots_into(locs);
1064 }
1065}
1066
1067impl<'t> Iterator for CaptureLocationMatches<'_, 't> {
1068 type Item = Match<'t>;
1069 fn next(&mut self) -> Option<Match<'t>> {
1070 let (a, b) = self.raw.advance()?;
1071 Some(Match {
1072 text: self.text,
1073 start: a,
1074 end: b,
1075 })
1076 }
1077}
1078
1079impl<'t> Iterator for CaptureMatches<'_, 't> {
1080 type Item = Captures<'t>;
1081 fn next(&mut self) -> Option<Captures<'t>> {
1082 self.raw.advance().map(|_| self.re.caps_from(self.text, &self.raw))
1083 }
1084}
1085
1086const FLAG_ICASE: u32 = 1;
1088const FLAG_MULTILINE: u32 = 2;
1089const FLAG_DOTALL: u32 = 4;
1090const FLAG_VERBOSE: u32 = 16;
1091const FLAG_ASCII: u32 = 64;
1092
1093pub struct RegexBuilder {
1095 pattern: String,
1096 flags: u32,
1097 #[cfg(feature = "fallback")]
1098 fallback: bool,
1099}
1100
1101impl RegexBuilder {
1102 pub fn new(pattern: &str) -> RegexBuilder {
1104 RegexBuilder {
1105 pattern: pattern.to_string(),
1106 flags: 0,
1107 #[cfg(feature = "fallback")]
1108 fallback: false,
1109 }
1110 }
1111
1112 fn set(&mut self, bit: u32, yes: bool) -> &mut RegexBuilder {
1113 if yes { self.flags |= bit } else { self.flags &= !bit }
1114 self
1115 }
1116
1117 #[cfg(feature = "fallback")]
1121 pub fn fallback(&mut self, yes: bool) -> &mut RegexBuilder {
1122 self.fallback = yes;
1123 self
1124 }
1125
1126 pub fn case_insensitive(&mut self, yes: bool) -> &mut RegexBuilder {
1128 self.set(FLAG_ICASE, yes)
1129 }
1130
1131 pub fn multi_line(&mut self, yes: bool) -> &mut RegexBuilder {
1133 self.set(FLAG_MULTILINE, yes)
1134 }
1135
1136 pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut RegexBuilder {
1138 self.set(FLAG_DOTALL, yes)
1139 }
1140
1141 pub fn ignore_whitespace(&mut self, yes: bool) -> &mut RegexBuilder {
1143 self.set(FLAG_VERBOSE, yes)
1144 }
1145
1146 pub fn unicode(&mut self, yes: bool) -> &mut RegexBuilder {
1149 self.set(FLAG_ASCII, !yes)
1150 }
1151
1152 pub fn size_limit(&mut self, _bytes: usize) -> &mut RegexBuilder {
1155 self
1156 }
1157
1158 pub fn build(&self) -> Result<Regex, Error> {
1160 match Regex::with_flags(&self.pattern, self.flags) {
1161 Ok(re) => Ok(re),
1162 Err(e) => {
1163 #[cfg(feature = "fallback")]
1164 if self.fallback && e.is_unsupported() {
1165 return Regex::build_fallback(&self.pattern, self.flags);
1166 }
1167 Err(e)
1168 }
1169 }
1170 }
1171}
1172
1173use std::borrow::Cow;
1175
1176pub trait Replacer {
1180 fn replace_append(&mut self, caps: &Captures, dst: &mut String);
1182}
1183
1184pub struct NoExpand<'a>(pub &'a str);
1186
1187impl Replacer for NoExpand<'_> {
1188 fn replace_append(&mut self, _caps: &Captures, dst: &mut String) {
1189 dst.push_str(self.0);
1190 }
1191}
1192
1193impl Replacer for &str {
1194 fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
1195 expand(caps, self, dst);
1196 }
1197}
1198
1199impl Replacer for String {
1200 fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
1201 expand(caps, self, dst);
1202 }
1203}
1204
1205impl<F, T> Replacer for F
1206where
1207 F: FnMut(&Captures) -> T,
1208 T: AsRef<str>,
1209{
1210 fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
1211 dst.push_str((*self)(caps).as_ref());
1212 }
1213}
1214
1215fn expand(caps: &Captures, template: &str, dst: &mut String) {
1218 let mut rest = template;
1219 while let Some(i) = rest.find('$') {
1220 dst.push_str(&rest[..i]);
1221 rest = &rest[i + 1..];
1222 if let Some(stripped) = rest.strip_prefix('$') {
1223 dst.push('$');
1224 rest = stripped;
1225 continue;
1226 }
1227 let (name, after) = if let Some(braced) = rest.strip_prefix('{') {
1228 match braced.find('}') {
1229 Some(j) => (&braced[..j], &braced[j + 1..]),
1230 None => {
1231 dst.push('$');
1232 ("", rest)
1233 }
1234 }
1235 } else {
1236 let end = rest.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_')).unwrap_or(rest.len());
1237 (&rest[..end], &rest[end..])
1238 };
1239 rest = after;
1240 if name.is_empty() {
1241 dst.push('$');
1242 continue;
1243 }
1244 let m = match name.parse::<usize>() {
1245 Ok(n) => caps.get(n),
1246 Err(_) => caps.name(name),
1247 };
1248 if let Some(m) = m {
1249 dst.push_str(m.as_str());
1250 }
1251 }
1252 dst.push_str(rest);
1253}
1254
1255impl Regex {
1256 pub fn replace<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
1259 self.replacen(text, 1, rep)
1260 }
1261
1262 pub fn replace_all<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
1264 self.replacen(text, 0, rep)
1265 }
1266
1267 pub fn replacen<'t, R: Replacer>(&self, text: &'t str, limit: usize, mut rep: R) -> Cow<'t, str> {
1269 let mut out: Option<String> = None;
1270 let mut last = 0;
1271 for (i, caps) in self.captures_iter(text).enumerate() {
1272 if limit != 0 && i >= limit {
1273 break;
1274 }
1275 let m = caps.get(0).unwrap();
1276 let dst = out.get_or_insert_with(|| String::with_capacity(text.len()));
1277 dst.push_str(&text[last..m.start()]);
1278 rep.replace_append(&caps, dst);
1279 last = m.end();
1280 }
1281 match out {
1282 Some(mut dst) => {
1283 dst.push_str(&text[last..]);
1284 Cow::Owned(dst)
1285 }
1286 None => Cow::Borrowed(text),
1287 }
1288 }
1289
1290 pub fn split<'r, 't>(&'r self, text: &'t str) -> Split<'r, 't> {
1293 Split { text, it: self.find_iter(text), last: 0, done: false }
1294 }
1295
1296 pub fn splitn<'r, 't>(&'r self, text: &'t str, limit: usize) -> SplitN<'r, 't> {
1299 SplitN { inner: self.split(text), limit, n: 0 }
1300 }
1301}
1302
1303pub struct Split<'r, 't> {
1305 text: &'t str,
1306 it: Matches<'r, 't>,
1307 last: usize,
1308 done: bool,
1309}
1310
1311impl<'t> Iterator for Split<'_, 't> {
1312 type Item = &'t str;
1313 fn next(&mut self) -> Option<&'t str> {
1314 if self.done {
1315 return None;
1316 }
1317 match self.it.next() {
1318 Some(m) => {
1319 let piece = &self.text[self.last..m.start()];
1320 self.last = m.end();
1321 Some(piece)
1322 }
1323 None => {
1324 self.done = true;
1325 Some(&self.text[self.last..])
1326 }
1327 }
1328 }
1329}
1330
1331pub struct SplitN<'r, 't> {
1333 inner: Split<'r, 't>,
1334 limit: usize,
1335 n: usize,
1336}
1337
1338impl<'t> Iterator for SplitN<'_, 't> {
1339 type Item = &'t str;
1340 fn next(&mut self) -> Option<&'t str> {
1341 if self.n >= self.limit {
1342 return None;
1343 }
1344 self.n += 1;
1345 if self.n == self.limit {
1346 if self.inner.done {
1348 return None;
1349 }
1350 self.inner.done = true;
1351 return Some(&self.inner.text[self.inner.last..]);
1352 }
1353 self.inner.next()
1354 }
1355}
1356
1357pub mod bytes {
1361 use super::{
1362 compile_handle, real_find_iter, real_find_iter_at, real_free, CaptureLocations, Error,
1363 GroupInfo, RawSpans, RealRegex, SlotStore, FLAG_ASCII, FLAG_DOTALL, FLAG_ICASE,
1364 FLAG_MULTILINE, FLAG_VERBOSE,
1365 };
1366 use std::borrow::Cow;
1367 use std::marker::PhantomData;
1368 use std::ops::Index;
1369 use std::os::raw::c_char;
1370 use std::sync::Arc;
1371
1372 const FLAG_BYTES: u32 = 8;
1373
1374 pub struct Regex {
1376 handle: *mut RealRegex,
1377 ngroups: usize,
1378 pattern: Vec<u8>,
1379 groups: Arc<GroupInfo>,
1380 }
1381
1382 unsafe impl Send for Regex {}
1383 unsafe impl Sync for Regex {}
1384
1385 impl Regex {
1386 pub fn new(pattern: &str) -> Result<Regex, Error> {
1388 Regex::with_flags(pattern.as_bytes(), 0)
1389 }
1390
1391 pub fn with_flags(pattern: &[u8], flags: u32) -> Result<Regex, Error> {
1393 let (handle, ngroups, groups) = compile_handle(pattern, flags | FLAG_BYTES)?;
1394 Ok(Regex { handle, ngroups, pattern: pattern.to_vec(), groups })
1395 }
1396
1397 pub fn as_bytes(&self) -> &[u8] {
1399 &self.pattern
1400 }
1401
1402 pub fn captures_len(&self) -> usize {
1404 self.ngroups
1405 }
1406
1407 pub fn capture_names(&self) -> impl Iterator<Item = Option<&str>> {
1409 self.groups.names.iter().map(|o| o.as_deref())
1410 }
1411
1412 fn raw<'r, 't>(&'r self, text: &'t [u8], start: Option<usize>) -> RawSpans<'r, 't> {
1413 let iter = unsafe {
1414 match start {
1415 None => real_find_iter(self.handle, text.as_ptr() as *const c_char, text.len()),
1416 Some(s) => real_find_iter_at(self.handle, text.as_ptr() as *const c_char, text.len(), s),
1417 }
1418 };
1419 assert!(!iter.is_null(), "real-regex: engine iteration failed");
1421 RawSpans { iter, handle: self.handle, text, ngroups: self.ngroups, buf: vec![0usize; 2 * self.ngroups], last_end: None, drive_pos: None, utf8: false, _re: PhantomData }
1422 }
1423
1424 fn caps_from<'t>(&self, text: &'t [u8], raw: &RawSpans<'_, '_>) -> Captures<'t> {
1425 Captures { text, slots: SlotStore::from_flat(&raw.buf), groups: Arc::clone(&self.groups) }
1427 }
1428
1429 pub fn is_match(&self, text: &[u8]) -> bool {
1431 self.raw(text, None).advance().is_some()
1432 }
1433
1434 pub fn find<'t>(&self, text: &'t [u8]) -> Option<Match<'t>> {
1436 self.raw(text, None).advance().map(|(a, b)| Match { text, start: a, end: b })
1437 }
1438
1439 pub fn find_at<'t>(&self, text: &'t [u8], start: usize) -> Option<Match<'t>> {
1441 self.raw(text, Some(start)).advance().map(|(a, b)| Match { text, start: a, end: b })
1442 }
1443
1444 pub fn find_iter<'r, 't>(&'r self, text: &'t [u8]) -> Matches<'r, 't> {
1446 Matches { raw: self.raw(text, None), text }
1447 }
1448
1449 pub fn is_match_at(&self, text: &[u8], start: usize) -> bool {
1451 self.raw(text, Some(start)).advance().is_some()
1452 }
1453
1454 pub fn captures<'t>(&self, text: &'t [u8]) -> Option<Captures<'t>> {
1456 {
1457 let mut c = self.raw(text, None);
1458 c.advance().map(|_| self.caps_from(text, &c))
1459 }
1460 }
1461
1462 pub fn captures_at<'t>(&self, text: &'t [u8], start: usize) -> Option<Captures<'t>> {
1464 {
1465 let mut c = self.raw(text, Some(start));
1466 c.advance().map(|_| self.caps_from(text, &c))
1467 }
1468 }
1469
1470 pub fn capture_locations(&self) -> CaptureLocations {
1472 CaptureLocations {
1473 slots: vec![0; 2 * self.ngroups],
1474 ngroups: self.ngroups,
1475 }
1476 }
1477
1478 pub fn captures_read<'t>(
1480 &self,
1481 locs: &mut CaptureLocations,
1482 text: &'t [u8],
1483 ) -> Option<Match<'t>> {
1484 self.captures_read_at(locs, text, 0)
1485 }
1486
1487 pub fn captures_read_at<'t>(
1489 &self,
1490 locs: &mut CaptureLocations,
1491 text: &'t [u8],
1492 start: usize,
1493 ) -> Option<Match<'t>> {
1494 locs.ensure(self.ngroups);
1495 let mut c = self.raw(text, if start == 0 { None } else { Some(start) });
1496 let (a, b) = c.advance()?;
1497 locs.slots.copy_from_slice(&c.buf);
1498 Some(Match {
1499 text,
1500 start: a,
1501 end: b,
1502 })
1503 }
1504
1505 pub fn captures_read_iter<'r, 't>(
1507 &'r self,
1508 text: &'t [u8],
1509 ) -> CaptureLocationMatches<'r, 't> {
1510 CaptureLocationMatches {
1511 raw: self.raw(text, None),
1512 text,
1513 ngroups: self.ngroups,
1514 }
1515 }
1516
1517 pub fn captures_iter<'r, 't>(&'r self, text: &'t [u8]) -> CaptureMatches<'r, 't> {
1519 CaptureMatches { raw: self.raw(text, None), re: self, text }
1520 }
1521
1522 pub fn shortest_match(&self, text: &[u8]) -> Option<usize> {
1525 self.raw(text, None).advance().map(|(_, e)| e)
1526 }
1527
1528 pub fn replace<'t, R: Replacer>(&self, text: &'t [u8], rep: R) -> Cow<'t, [u8]> {
1531 self.replacen(text, 1, rep)
1532 }
1533
1534 pub fn replace_all<'t, R: Replacer>(&self, text: &'t [u8], rep: R) -> Cow<'t, [u8]> {
1536 self.replacen(text, 0, rep)
1537 }
1538
1539 pub fn replacen<'t, R: Replacer>(&self, text: &'t [u8], limit: usize, mut rep: R) -> Cow<'t, [u8]> {
1541 let mut out: Option<Vec<u8>> = None;
1542 let mut last = 0;
1543 for (i, caps) in self.captures_iter(text).enumerate() {
1544 if limit != 0 && i >= limit {
1545 break;
1546 }
1547 let m = caps.get(0).unwrap();
1548 let dst = out.get_or_insert_with(|| Vec::with_capacity(text.len()));
1549 dst.extend_from_slice(&text[last..m.start()]);
1550 rep.replace_append(&caps, dst);
1551 last = m.end();
1552 }
1553 match out {
1554 Some(mut dst) => {
1555 dst.extend_from_slice(&text[last..]);
1556 Cow::Owned(dst)
1557 }
1558 None => Cow::Borrowed(text),
1559 }
1560 }
1561
1562 pub fn split<'r, 't>(&'r self, text: &'t [u8]) -> Split<'r, 't> {
1564 Split { text, it: self.find_iter(text), last: 0, done: false }
1565 }
1566
1567 pub fn splitn<'r, 't>(&'r self, text: &'t [u8], limit: usize) -> SplitN<'r, 't> {
1570 SplitN { inner: self.split(text), limit, n: 0 }
1571 }
1572 }
1573
1574 impl Drop for Regex {
1575 fn drop(&mut self) {
1576 unsafe { real_free(self.handle) }
1577 }
1578 }
1579
1580 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1582 pub struct Match<'t> {
1583 text: &'t [u8],
1584 start: usize,
1585 end: usize,
1586 }
1587
1588 impl<'t> Match<'t> {
1589 pub fn start(&self) -> usize { self.start }
1591 pub fn end(&self) -> usize { self.end }
1593 pub fn as_bytes(&self) -> &'t [u8] { &self.text[self.start..self.end] }
1595 pub fn range(&self) -> std::ops::Range<usize> { self.start..self.end }
1597 pub fn is_empty(&self) -> bool { self.start == self.end }
1599 pub fn len(&self) -> usize { self.end - self.start }
1601 }
1602
1603 pub struct Captures<'t> {
1605 text: &'t [u8],
1606 slots: SlotStore,
1607 groups: Arc<GroupInfo>,
1608 }
1609
1610 impl<'t> Captures<'t> {
1611 pub fn get(&self, i: usize) -> Option<Match<'t>> {
1613 self.slots.group(i).map(|(s, e)| Match { text: self.text, start: s, end: e })
1614 }
1615 pub fn name(&self, name: &str) -> Option<Match<'t>> {
1617 self.groups.by_name.get(name).and_then(|&i| self.get(i))
1618 }
1619 pub fn len(&self) -> usize { self.slots.ngroups() }
1621 pub fn is_empty(&self) -> bool { self.slots.ngroups() == 0 }
1623 }
1624
1625 impl Index<usize> for Captures<'_> {
1626 type Output = [u8];
1627 fn index(&self, i: usize) -> &[u8] {
1628 self.get(i).map(|m| m.as_bytes()).unwrap_or_else(|| panic!("no group at index {i}"))
1629 }
1630 }
1631
1632 impl Index<&str> for Captures<'_> {
1633 type Output = [u8];
1634 fn index(&self, name: &str) -> &[u8] {
1635 self.name(name).map(|m| m.as_bytes()).unwrap_or_else(|| panic!("no group named {name:?}"))
1636 }
1637 }
1638
1639 pub struct Matches<'r, 't> {
1641 raw: RawSpans<'r, 't>,
1642 text: &'t [u8],
1643 }
1644
1645 impl<'t> Iterator for Matches<'_, 't> {
1646 type Item = Match<'t>;
1647 fn next(&mut self) -> Option<Match<'t>> {
1648 self.raw.advance().map(|(a, b)| Match { text: self.text, start: a, end: b })
1649 }
1650 }
1651
1652 pub struct CaptureMatches<'r, 't> {
1654 raw: RawSpans<'r, 't>,
1655 re: &'r Regex,
1656 text: &'t [u8],
1657 }
1658
1659 pub struct CaptureLocationMatches<'r, 't> {
1661 raw: RawSpans<'r, 't>,
1662 text: &'t [u8],
1663 ngroups: usize,
1664 }
1665
1666 impl CaptureLocationMatches<'_, '_> {
1667 pub fn len(&self) -> usize {
1669 self.ngroups
1670 }
1671
1672 pub fn get(&self, i: usize) -> Option<(usize, usize)> {
1674 if i >= self.ngroups {
1675 return None;
1676 }
1677 let a = self.raw.buf[2 * i];
1678 let b = self.raw.buf[2 * i + 1];
1679 if a == usize::MAX {
1680 None
1681 } else {
1682 Some((a, b))
1683 }
1684 }
1685
1686 pub fn read_captures(&self, locs: &mut CaptureLocations) {
1688 locs.ensure(self.ngroups);
1689 locs.slots.copy_from_slice(&self.raw.buf);
1690 }
1691 }
1692
1693 impl<'t> Iterator for CaptureLocationMatches<'_, 't> {
1694 type Item = Match<'t>;
1695 fn next(&mut self) -> Option<Match<'t>> {
1696 let (a, b) = self.raw.advance()?;
1697 Some(Match {
1698 text: self.text,
1699 start: a,
1700 end: b,
1701 })
1702 }
1703 }
1704
1705 impl<'t> Iterator for CaptureMatches<'_, 't> {
1706 type Item = Captures<'t>;
1707 fn next(&mut self) -> Option<Captures<'t>> {
1708 self.raw.advance().map(|_| self.re.caps_from(self.text, &self.raw))
1709 }
1710 }
1711
1712 pub struct Split<'r, 't> {
1714 text: &'t [u8],
1715 it: Matches<'r, 't>,
1716 last: usize,
1717 done: bool,
1718 }
1719
1720 impl<'t> Iterator for Split<'_, 't> {
1721 type Item = &'t [u8];
1722 fn next(&mut self) -> Option<&'t [u8]> {
1723 if self.done {
1724 return None;
1725 }
1726 match self.it.next() {
1727 Some(m) => {
1728 let piece = &self.text[self.last..m.start()];
1729 self.last = m.end();
1730 Some(piece)
1731 }
1732 None => {
1733 self.done = true;
1734 Some(&self.text[self.last..])
1735 }
1736 }
1737 }
1738 }
1739
1740 pub struct SplitN<'r, 't> {
1742 inner: Split<'r, 't>,
1743 limit: usize,
1744 n: usize,
1745 }
1746
1747 impl<'t> Iterator for SplitN<'_, 't> {
1748 type Item = &'t [u8];
1749 fn next(&mut self) -> Option<&'t [u8]> {
1750 if self.n >= self.limit {
1751 return None;
1752 }
1753 self.n += 1;
1754 if self.n == self.limit {
1755 if self.inner.done {
1756 return None;
1757 }
1758 self.inner.done = true;
1759 return Some(&self.inner.text[self.inner.last..]);
1760 }
1761 self.inner.next()
1762 }
1763 }
1764
1765 pub trait Replacer {
1768 fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>);
1770 }
1771
1772 pub struct NoExpand<'a>(pub &'a [u8]);
1774
1775 impl Replacer for NoExpand<'_> {
1776 fn replace_append(&mut self, _caps: &Captures, dst: &mut Vec<u8>) {
1777 dst.extend_from_slice(self.0);
1778 }
1779 }
1780
1781 impl Replacer for &[u8] {
1782 fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
1783 expand_bytes(caps, self, dst);
1784 }
1785 }
1786
1787 impl<F, T> Replacer for F
1788 where
1789 F: FnMut(&Captures) -> T,
1790 T: AsRef<[u8]>,
1791 {
1792 fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
1793 dst.extend_from_slice((*self)(caps).as_ref());
1794 }
1795 }
1796
1797 fn expand_bytes(caps: &Captures, template: &[u8], dst: &mut Vec<u8>) {
1800 let mut i = 0;
1801 while i < template.len() {
1802 let b = template[i];
1803 if b != b'$' {
1804 dst.push(b);
1805 i += 1;
1806 continue;
1807 }
1808 i += 1; if i < template.len() && template[i] == b'$' {
1810 dst.push(b'$');
1811 i += 1;
1812 continue;
1813 }
1814 let (name, next) = if i < template.len() && template[i] == b'{' {
1815 match template[i + 1..].iter().position(|&c| c == b'}') {
1816 Some(j) => (&template[i + 1..i + 1 + j], i + 1 + j + 1),
1817 None => {
1818 dst.push(b'$');
1819 continue;
1820 }
1821 }
1822 } else {
1823 let mut j = i;
1824 while j < template.len() && (template[j].is_ascii_alphanumeric() || template[j] == b'_') {
1825 j += 1;
1826 }
1827 (&template[i..j], j)
1828 };
1829 i = next;
1830 if name.is_empty() {
1831 dst.push(b'$');
1832 continue;
1833 }
1834 let name_str = std::str::from_utf8(name).unwrap_or("");
1835 let m = match name_str.parse::<usize>() {
1836 Ok(n) => caps.get(n),
1837 Err(_) => caps.name(name_str),
1838 };
1839 if let Some(m) = m {
1840 dst.extend_from_slice(m.as_bytes());
1841 }
1842 }
1843 }
1844
1845 pub struct RegexBuilder {
1847 pattern: Vec<u8>,
1848 flags: u32,
1849 }
1850
1851 impl RegexBuilder {
1852 pub fn new(pattern: &str) -> RegexBuilder {
1854 RegexBuilder { pattern: pattern.as_bytes().to_vec(), flags: 0 }
1855 }
1856 fn set(&mut self, bit: u32, yes: bool) -> &mut RegexBuilder {
1857 if yes { self.flags |= bit } else { self.flags &= !bit }
1858 self
1859 }
1860 pub fn case_insensitive(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_ICASE, yes) }
1862 pub fn multi_line(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_MULTILINE, yes) }
1864 pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_DOTALL, yes) }
1866 pub fn ignore_whitespace(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_VERBOSE, yes) }
1868 pub fn unicode(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_ASCII, !yes) }
1870 pub fn size_limit(&mut self, _bytes: usize) -> &mut RegexBuilder { self }
1872 pub fn build(&self) -> Result<Regex, Error> {
1874 Regex::with_flags(&self.pattern, self.flags)
1875 }
1876 }
1877}