use crate::classicalbacktrack;
use crate::emit;
use crate::exec;
use crate::indexing;
use crate::insn::CompiledRegex;
use crate::optimizer;
use crate::parse;
#[cfg(feature = "backend-pikevm")]
use crate::pikevm;
use std::collections::hash_map::Iter;
use std::collections::HashMap;
use std::{fmt, str::FromStr};
pub use parse::Error;
#[derive(Debug, Copy, Clone, Default)]
pub struct Flags {
pub icase: bool,
pub multiline: bool,
pub dot_all: bool,
pub no_opt: bool,
}
impl From<&str> for Flags {
#[inline]
fn from(s: &str) -> Self {
let mut result = Self::default();
for c in s.chars() {
match c {
'm' => {
result.multiline = true;
}
'i' => {
result.icase = true;
}
's' => {
result.dot_all = true;
}
_ => {
}
}
}
result
}
}
impl fmt::Display for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.multiline {
f.write_str("m")?;
}
if self.icase {
f.write_str("i")?;
}
Ok(())
}
}
pub type Range = std::ops::Range<usize>;
pub type Matches<'r, 't> = exec::Matches<backends::DefaultExecutor<'r, 't>>;
pub type AsciiMatches<'r, 't> = exec::Matches<backends::DefaultAsciiExecutor<'r, 't>>;
#[derive(Debug, Clone)]
pub struct Match {
pub range: Range,
pub captures: Vec<Option<Range>>,
pub(crate) named_captures: HashMap<String, u16>,
}
impl Match {
#[inline]
pub fn group(&self, idx: usize) -> Option<Range> {
if idx == 0 {
Some(self.range.clone())
} else {
self.captures[idx - 1].clone()
}
}
#[inline]
pub fn named_group(&self, name: &str) -> Option<Range> {
let idx = *self.named_captures.get(name)?;
self.captures[idx as usize].clone()
}
#[inline]
pub fn named_groups(&self) -> NamedGroups {
NamedGroups::new(self)
}
#[inline]
pub fn range(&self) -> Range {
self.range.clone()
}
#[inline]
pub fn start(&self) -> usize {
self.range.start
}
#[inline]
pub fn end(&self) -> usize {
self.range.end
}
#[inline]
pub fn groups(&self) -> Groups {
Groups::new(self)
}
}
#[derive(Clone)]
pub struct Groups<'m> {
mat: &'m Match,
i: usize,
max: usize,
}
impl<'m> Groups<'m> {
#[inline]
fn new(mat: &'m Match) -> Self {
Self {
mat,
i: 0,
max: mat.captures.len() + 1,
}
}
}
impl<'m> Iterator for Groups<'m> {
type Item = Option<Range>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
if i < self.max {
self.i += 1;
Some(self.mat.group(i))
} else {
None
}
}
}
#[derive(Clone)]
pub struct NamedGroups<'m> {
mat: &'m Match,
named_groups_iter: Iter<'m, String, u16>,
}
impl<'m> NamedGroups<'m> {
#[inline]
fn new(mat: &'m Match) -> Self {
Self {
mat,
named_groups_iter: mat.named_captures.iter(),
}
}
}
impl<'m> Iterator for NamedGroups<'m> {
type Item = (&'m str, Option<Range>);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some((name, idx)) = self.named_groups_iter.next() {
if let Some(r) = self.mat.captures.get(*idx as usize) {
match r {
Some(range) => Some((name.as_str(), Some(range.clone()))),
None => Some((name.as_str(), None)),
}
} else {
Some((name.as_str(), None))
}
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct Regex {
cr: CompiledRegex,
}
impl From<CompiledRegex> for Regex {
fn from(cr: CompiledRegex) -> Self {
Self { cr }
}
}
impl Regex {
#[inline]
pub fn new(pattern: &str) -> Result<Regex, Error> {
Self::with_flags(pattern, Flags::default())
}
#[inline]
pub fn with_flags<F>(pattern: &str, flags: F) -> Result<Regex, Error>
where
F: Into<Flags>,
{
let flags = flags.into();
let mut ire = parse::try_parse(pattern, flags)?;
if !flags.no_opt {
optimizer::optimize(&mut ire);
}
let cr = emit::emit(&ire);
Ok(Regex { cr })
}
#[inline]
pub fn find(&self, text: &str) -> Option<Match> {
self.find_iter(text).next()
}
#[inline]
pub fn find_iter<'r, 't>(&'r self, text: &'t str) -> Matches<'r, 't> {
self.find_from(text, 0)
}
#[inline]
pub fn find_from<'r, 't>(&'r self, text: &'t str, start: usize) -> Matches<'r, 't> {
backends::find(self, text, start)
}
#[inline]
pub fn find_ascii(&self, text: &str) -> Option<Match> {
self.find_iter_ascii(text).next()
}
#[inline]
pub fn find_iter_ascii<'r, 't>(&'r self, text: &'t str) -> AsciiMatches<'r, 't> {
self.find_from_ascii(text, 0)
}
#[inline]
pub fn find_from_ascii<'r, 't>(&'r self, text: &'t str, start: usize) -> AsciiMatches<'r, 't> {
backends::find(self, text, start)
}
}
impl FromStr for Regex {
type Err = Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Error> {
Self::new(s)
}
}
#[doc(hidden)]
pub mod backends {
use super::exec;
use super::indexing;
use super::Regex;
pub use crate::emit::emit;
pub use crate::optimizer::optimize;
pub use crate::parse::try_parse;
pub type BacktrackExecutor<'r, 't> =
super::classicalbacktrack::BacktrackExecutor<'r, indexing::Utf8Input<'t>>;
#[cfg(feature = "backend-pikevm")]
pub type PikeVMExecutor<'r, 't> = super::pikevm::PikeVMExecutor<'r, indexing::Utf8Input<'t>>;
pub type DefaultExecutor<'r, 't> = BacktrackExecutor<'r, 't>;
pub type DefaultAsciiExecutor<'r, 't> =
<DefaultExecutor<'r, 't> as exec::Executor<'r, 't>>::AsAscii;
pub fn find<'r, 't, Executor: exec::Executor<'r, 't>>(
re: &'r Regex,
text: &'t str,
start: usize,
) -> exec::Matches<Executor> {
exec::Matches::new(Executor::new(&re.cr, text), start)
}
pub fn find_ascii<'r, 't, Executor: exec::Executor<'r, 't>>(
re: &'r Regex,
text: &'t str,
start: usize,
) -> exec::Matches<Executor::AsAscii> {
find::<Executor::AsAscii>(re, text, start)
}
}