use std::collections::BTreeMap;
use std::sync::Arc;
use onig::Regex;
use super::pattern::RegexMatch;
pub(super) type EndRegexCache = BTreeMap<String, Arc<Regex>>;
#[derive(Debug)]
pub(super) enum EndPattern {
Static {
source: String,
regex: Regex,
},
Dynamic {
source: String,
},
}
pub(super) enum EndRegex<'pat> {
Static {
source: &'pat str,
regex: &'pat Regex,
},
Dynamic {
source: String,
regex: Arc<Regex>,
},
}
impl EndPattern {
pub(super) fn compile(source: &str) -> Option<Self> {
if has_numeric_backrefs(source) {
return Some(Self::Dynamic {
source: source.to_owned(),
});
}
let regex = Regex::new(source).ok()?;
Some(Self::Static {
source: source.to_owned(),
regex,
})
}
pub(super) fn resolve_for_begin<'pat>(
&'pat self,
begin: &RegexMatch,
line: &str,
cache: &mut EndRegexCache,
) -> Option<EndRegex<'pat>> {
match self {
Self::Static { source, regex } => Some(EndRegex::Static { source, regex }),
Self::Dynamic { source } => {
let resolved = begin.expand_backrefs(source, line);
let regex = lookup_or_compile(cache, &resolved)?;
Some(EndRegex::Dynamic {
source: resolved,
regex,
})
}
}
}
pub(super) fn resume<'pat>(
&'pat self,
dynamic_source: Option<&str>,
cache: &mut EndRegexCache,
) -> Option<EndRegex<'pat>> {
match self {
Self::Static { source, regex } => Some(EndRegex::Static { source, regex }),
Self::Dynamic { .. } => {
let source = dynamic_source?;
let regex = lookup_or_compile(cache, source)?;
Some(EndRegex::Dynamic {
source: source.to_owned(),
regex,
})
}
}
}
pub(super) fn is_dynamic(&self) -> bool {
matches!(self, Self::Dynamic { .. })
}
}
impl EndRegex<'_> {
pub(super) fn source(&self) -> &str {
match self {
Self::Static { source, .. } => source,
Self::Dynamic { source, .. } => source.as_str(),
}
}
pub(super) fn regex(&self) -> &Regex {
match self {
Self::Static { regex, .. } => regex,
Self::Dynamic { regex, .. } => regex,
}
}
pub(super) fn dynamic_source(&self) -> Option<String> {
match self {
Self::Static { .. } => None,
Self::Dynamic { source, .. } => Some(source.clone()),
}
}
}
fn lookup_or_compile(cache: &mut EndRegexCache, source: &str) -> Option<Arc<Regex>> {
if let Some(regex) = cache.get(source) {
return Some(Arc::clone(regex));
}
let regex = Arc::new(Regex::new(source).ok()?);
cache.insert(source.to_owned(), Arc::clone(®ex));
Some(regex)
}
fn has_numeric_backrefs(source: &str) -> bool {
let mut backslashes = 0_usize;
for byte in source.bytes() {
if byte == b'\\' {
backslashes += 1;
} else {
if byte.is_ascii_digit() && backslashes % 2 == 1 {
return true;
}
backslashes = 0;
}
}
false
}