use std::collections::HashMap;
pub struct Match<'h> {
pub(crate) haystack: &'h str,
pub(crate) char_to_byte: Vec<usize>,
pub(crate) caps: Vec<Option<(usize, usize)>>,
pub(crate) log: Vec<Vec<(usize, usize)>>,
pub(crate) names: HashMap<String, usize>,
}
impl<'h> Match<'h> {
fn byte_span(&self, g: usize) -> Option<(usize, usize)> {
let (s, e) = self.caps.get(g).copied().flatten()?;
Some((self.char_to_byte[s], self.char_to_byte[e]))
}
pub fn as_str(&self) -> &'h str {
self.group(0).unwrap_or("")
}
pub fn group(&self, g: usize) -> Option<&'h str> {
let (s, e) = self.byte_span(g)?;
Some(&self.haystack[s..e])
}
pub fn name(&self, name: &str) -> Option<&'h str> {
let g = *self.names.get(name)?;
self.group(g)
}
pub fn start(&self) -> usize {
self.start_of(0)
}
pub fn end(&self) -> usize {
self.end_of(0)
}
pub fn span(&self) -> (usize, usize) {
(self.start(), self.end())
}
pub fn start_of(&self, g: usize) -> usize {
match self.byte_span(g) {
Some((s, _)) => s,
None => self.haystack.len(),
}
}
pub fn end_of(&self, g: usize) -> usize {
match self.byte_span(g) {
Some((_, e)) => e,
None => self.haystack.len(),
}
}
pub fn span_of(&self, g: usize) -> (usize, usize) {
(self.start_of(g), self.end_of(g))
}
pub fn len(&self) -> usize {
self.caps.len()
}
pub fn is_empty(&self) -> bool {
false
}
pub fn groups(&self) -> Vec<Option<&'h str>> {
(0..self.caps.len()).map(|i| self.group(i)).collect()
}
pub fn captures(&self, g: usize) -> Vec<Option<&'h str>> {
self.log
.get(g)
.map(|v| {
v.iter()
.map(|(s, e)| {
Some(&self.haystack[self.char_to_byte[*s]..self.char_to_byte[*e]])
})
.collect()
})
.unwrap_or_default()
}
pub fn captures_name(&self, name: &str) -> Vec<Option<&'h str>> {
match self.names.get(name) {
Some(&g) => self.captures(g),
None => Vec::new(),
}
}
pub fn starts(&self, g: usize) -> Vec<usize> {
self.log
.get(g)
.map(|v| v.iter().map(|(s, _)| self.char_to_byte[*s]).collect())
.unwrap_or_default()
}
pub fn ends(&self, g: usize) -> Vec<usize> {
self.log
.get(g)
.map(|v| v.iter().map(|(_, e)| self.char_to_byte[*e]).collect())
.unwrap_or_default()
}
pub fn spans(&self, g: usize) -> Vec<(usize, usize)> {
self.log
.get(g)
.map(|v| {
v.iter()
.map(|(s, e)| (self.char_to_byte[*s], self.char_to_byte[*e]))
.collect()
})
.unwrap_or_default()
}
pub fn named_groups(&self) -> HashMap<String, &'h str> {
let mut out = HashMap::new();
for (name, &g) in &self.names {
if let Some(s) = self.group(g) {
out.insert(name.clone(), s);
}
}
out
}
pub fn captures_dict(&self) -> HashMap<String, Vec<&'h str>> {
let mut out = HashMap::new();
for (name, &g) in &self.names {
let v: Vec<&'h str> = self.captures(g).into_iter().flatten().collect();
out.insert(name.clone(), v);
}
out
}
pub fn all_captures(&self) -> Vec<Vec<&'h str>> {
(0..self.caps.len())
.map(|g| self.captures(g).into_iter().flatten().collect())
.collect()
}
pub fn all_spans(&self) -> Vec<Vec<(usize, usize)>> {
(0..self.caps.len()).map(|g| self.spans(g)).collect()
}
pub fn group0(&self) -> &'h str {
self.as_str()
}
pub fn all_groups(&self) -> Vec<Option<&'h str>> {
(0..self.caps.len()).map(|i| self.group(i)).collect()
}
}
impl<'h> std::ops::Index<usize> for Match<'h> {
type Output = str;
fn index(&self, i: usize) -> &str {
self.group(i).unwrap_or("")
}
}
impl<'h> std::ops::Index<&str> for Match<'h> {
type Output = str;
fn index(&self, name: &str) -> &str {
self.name(name).unwrap_or("")
}
}
impl<'h> std::fmt::Debug for Match<'h> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (s, e) = self.span();
write!(f, "Match {:?} span={}..{}", &self.haystack[s..e], s, e)
}
}
pub struct FindIter<'r, 'h> {
pub(crate) re: &'r crate::Regex,
pub(crate) haystack: &'h str,
pub(crate) st: crate::state::State,
pub(crate) pos: usize,
pub(crate) last_end: Option<usize>,
}
impl<'r, 'h> Iterator for FindIter<'r, 'h> {
type Item = Match<'h>;
fn next(&mut self) -> Option<Match<'h>> {
if let Some((start, end)) = self.re.find_from(&mut self.st, self.pos) {
let m = Match {
haystack: self.haystack,
char_to_byte: self.st.char_to_byte.clone(),
caps: self.st.caps.clone(),
log: self.st.log.clone(),
names: self.re.names_clone(),
};
self.pos = if end == start { end + 1 } else { end };
self.last_end = Some(end);
Some(m)
} else {
None
}
}
}
pub type CaptureMatches<'r, 'h> = FindIter<'r, 'h>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MatchStatus {
Full,
Partial,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GroupMatch<'h> {
Matched(&'h str),
Partial(&'h str),
None,
}
pub struct PartialMatch<'h> {
pub status: MatchStatus,
pub matched: &'h str,
pub start: usize,
pub end: usize,
pub groups: Vec<GroupMatch<'h>>,
pub(crate) names: HashMap<String, usize>,
}
impl<'h> PartialMatch<'h> {
pub fn is_full(&self) -> bool {
matches!(self.status, MatchStatus::Full)
}
pub fn is_partial(&self) -> bool {
matches!(self.status, MatchStatus::Partial)
}
pub fn group(&self, g: usize) -> Option<&'h str> {
match self.groups.get(g)? {
GroupMatch::Matched(s) | GroupMatch::Partial(s) => Some(*s),
GroupMatch::None => None,
}
}
pub fn name(&self, name: &str) -> Option<&'h str> {
let g = *self.names.get(name)?;
self.group(g)
}
pub fn group_matched(&self, g: usize) -> bool {
matches!(self.groups.get(g), Some(GroupMatch::Matched(_)))
}
pub fn group_partial(&self, g: usize) -> bool {
matches!(self.groups.get(g), Some(GroupMatch::Partial(_)))
}
pub fn group_none(&self, g: usize) -> bool {
matches!(self.groups.get(g), Some(GroupMatch::None) | None)
}
}
impl<'h> std::fmt::Debug for PartialMatch<'h> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"PartialMatch {:?} status={:?} span={}..{}",
self.matched, self.status, self.start, self.end
)
}
}