use std::marker::PhantomData;
use std::os::raw::c_char;
enum RealRegex {}
enum RealIter {}
extern "C" {
fn real_compile(pattern: *const c_char, len: usize, flags: u32,
errbuf: *mut c_char, errbuf_len: usize) -> *mut RealRegex;
fn real_group_count(re: *const RealRegex) -> usize;
fn real_free(re: *mut RealRegex);
fn real_find_iter(re: *const RealRegex, text: *const c_char, len: usize) -> *mut RealIter;
fn real_iter_next(iter: *mut RealIter, spans: *mut usize) -> i32;
fn real_iter_free(iter: *mut RealIter);
}
pub struct Regex {
handle: *mut RealRegex,
ngroups: usize, }
unsafe impl Send for Regex {}
unsafe impl Sync for Regex {}
impl Regex {
pub fn new(pattern: &str) -> Result<Regex, String> {
Regex::with_flags(pattern, 0)
}
pub fn with_flags(pattern: &str, flags: u32) -> Result<Regex, String> {
let mut err = [0u8; 256];
let handle = unsafe {
real_compile(pattern.as_ptr() as *const c_char, pattern.len(), flags,
err.as_mut_ptr() as *mut c_char, err.len())
};
if handle.is_null() {
let end = err.iter().position(|&b| b == 0).unwrap_or(err.len());
return Err(String::from_utf8_lossy(&err[..end]).into_owned());
}
let ngroups = unsafe { real_group_count(handle) };
Ok(Regex { handle, ngroups })
}
pub fn captures_len(&self) -> usize {
self.ngroups - 1
}
pub fn find_iter<'r, 't>(&'r self, text: &'t str) -> Matches<'r, 't> {
let iter = unsafe {
real_find_iter(self.handle, text.as_ptr() as *const c_char, text.len())
};
Matches { iter, ngroups: self.ngroups, text, _re: PhantomData }
}
pub fn is_match(&self, text: &str) -> bool {
self.find_iter(text).next().is_some()
}
}
impl Drop for Regex {
fn drop(&mut self) {
unsafe { real_free(self.handle) }
}
}
pub struct Match<'t> {
spans: Vec<Option<(usize, usize)>>,
text: &'t str,
}
impl<'t> Match<'t> {
pub fn get(&self, i: usize) -> Option<&'t str> {
self.spans.get(i).copied().flatten().map(|(a, b)| &self.text[a..b])
}
pub fn span(&self, i: usize) -> Option<(usize, usize)> {
self.spans.get(i).copied().flatten()
}
pub fn len(&self) -> usize {
self.spans.len()
}
pub fn is_empty(&self) -> bool {
self.spans.is_empty()
}
}
pub struct Matches<'r, 't> {
iter: *mut RealIter,
ngroups: usize,
text: &'t str,
_re: PhantomData<&'r Regex>,
}
impl<'t> Iterator for Matches<'_, 't> {
type Item = Match<'t>;
fn next(&mut self) -> Option<Match<'t>> {
let mut spans = vec![0usize; 2 * self.ngroups];
let got = unsafe { real_iter_next(self.iter, spans.as_mut_ptr()) };
if got == 0 {
return None;
}
let groups = (0..self.ngroups)
.map(|g| {
let (a, b) = (spans[2 * g], spans[2 * g + 1]);
if a == usize::MAX { None } else { Some((a, b)) }
})
.collect();
Some(Match { spans: groups, text: self.text })
}
}
impl Drop for Matches<'_, '_> {
fn drop(&mut self) {
unsafe { real_iter_free(self.iter) }
}
}