use crate::{
Regex,
err::{BindingErrorCode, ErrorKind, RegexError, Result},
flags::RegexecFlags,
tre,
};
pub type RegMatchStr<'a> = Vec<Option<&'a str>>;
pub type RegMatchBytes<'a> = Vec<Option<&'a [u8]>>;
pub fn match_offset(offset: tre::regoff_t) -> Result<usize> {
usize::try_from(offset).map_err(|error| {
RegexError::new(
ErrorKind::Binding(BindingErrorCode::INVALID_MATCH_OFFSET),
&format!("Invalid match offset: {error}"),
)
})
}
impl Regex {
pub fn is_match(&self, string: &str, flags: RegexecFlags) -> Result<bool> {
match self.regexec(string, 0, flags) {
Ok(_) => Ok(true),
Err(RegexError {
kind: ErrorKind::Tre(tre::reg_errcode_t::REG_NOMATCH),
..
}) => Ok(false),
Err(error) => Err(error),
}
}
pub fn is_match_bytes(&self, data: &[u8], flags: RegexecFlags) -> Result<bool> {
match self.regexec_bytes(data, 0, flags) {
Ok(_) => Ok(true),
Err(RegexError {
kind: ErrorKind::Tre(tre::reg_errcode_t::REG_NOMATCH),
..
}) => Ok(false),
Err(error) => Err(error),
}
}
pub fn captures<'a>(
&self,
string: &'a str,
capacity: usize,
flags: RegexecFlags,
) -> Result<RegMatchStr<'a>> {
self.regexec(string, capacity, flags)
}
pub fn captures_bytes<'a>(
&self,
data: &'a [u8],
capacity: usize,
flags: RegexecFlags,
) -> Result<RegMatchBytes<'a>> {
self.regexec_bytes(data, capacity, flags)
}
#[inline]
pub fn regexec<'a>(
&self,
string: &'a str,
nmatches: usize,
flags: RegexecFlags,
) -> Result<RegMatchStr<'a>> {
let Some(compiled_reg_obj) = self.as_raw() else {
return Err(RegexError::new(
ErrorKind::Binding(BindingErrorCode::REGEX_VACANT),
"Attempted to unwrap a vacant Regex object",
));
};
let data = string.as_bytes();
let mut match_vec = vec![tre::regmatch_t::default(); nmatches];
let result = unsafe {
tre::tre_regnexec(
compiled_reg_obj,
data.as_ptr().cast(),
data.len(),
nmatches,
match_vec.as_mut_ptr(),
flags.bits(),
)
};
if result != 0 {
return Err(self.regerror(result));
}
let mut result = Vec::with_capacity(nmatches);
for pmatch in match_vec {
if pmatch.rm_so < 0 || pmatch.rm_eo < 0 {
result.push(None);
continue;
}
let start_offset = match_offset(pmatch.rm_so)?;
let end_offset = match_offset(pmatch.rm_eo)?;
let matched = string.get(start_offset..end_offset).ok_or_else(|| {
RegexError::new(
ErrorKind::Binding(BindingErrorCode::ENCODING),
"TRE returned match offsets that are not UTF-8 character boundaries",
)
})?;
result.push(Some(matched));
}
Ok(result)
}
pub fn regexec_bytes<'a>(
&self,
data: &'a [u8],
nmatches: usize,
flags: RegexecFlags,
) -> Result<RegMatchBytes<'a>> {
let Some(compiled_reg_obj) = self.as_raw() else {
return Err(RegexError::new(
ErrorKind::Binding(BindingErrorCode::REGEX_VACANT),
"Attempted to unwrap a vacant Regex object",
));
};
let mut match_vec: Vec<tre::regmatch_t> =
vec![tre::regmatch_t { rm_so: 0, rm_eo: 0 }; nmatches];
let result = unsafe {
tre::tre_regnexecb(
compiled_reg_obj,
data.as_ptr().cast::<std::ffi::c_char>(),
data.len(),
nmatches,
match_vec.as_mut_ptr(),
flags.bits(),
)
};
if result != 0 {
return Err(self.regerror(result));
}
let mut result = Vec::with_capacity(nmatches);
for pmatch in match_vec {
if pmatch.rm_so < 0 || pmatch.rm_eo < 0 {
result.push(None);
continue;
}
let start_offset = match_offset(pmatch.rm_so)?;
let end_offset = match_offset(pmatch.rm_eo)?;
result.push(Some(&data[start_offset..end_offset]));
}
Ok(result)
}
}
#[inline]
pub fn regexec<'a>(
compiled_reg: &Regex,
string: &'a str,
nmatches: usize,
flags: RegexecFlags,
) -> Result<RegMatchStr<'a>> {
compiled_reg.regexec(string, nmatches, flags)
}
pub fn regexec_bytes<'a>(
compiled_reg: &Regex,
data: &'a [u8],
nmatches: usize,
flags: RegexecFlags,
) -> Result<RegMatchBytes<'a>> {
compiled_reg.regexec_bytes(data, nmatches, flags)
}