use std::ffi::{c_char, c_int, c_uint, CString};
use std::fmt;
use std::ptr::null_mut;
use crate::{tre, Regex};
pub type ErrorInt = c_int;
pub type Result<T> = std::result::Result<T, RegexError>;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BindingErrorCode(u32);
impl BindingErrorCode {
pub const CSTRING: Self = Self(1);
pub const ENCODING: Self = Self(2);
pub const REGEX_VACANT: Self = Self(3);
}
#[derive(Debug, PartialEq, Eq)]
pub enum ErrorKind {
Binding(BindingErrorCode),
Tre(tre::reg_errcode_t),
}
#[derive(Debug, PartialEq, Eq)]
pub struct RegexError {
pub kind: ErrorKind,
pub error: String,
}
impl RegexError {
#[must_use]
#[inline]
pub fn new(kind: ErrorKind, error: &str) -> Self {
Self {
kind,
error: error.to_string(),
}
}
}
impl std::error::Error for RegexError {}
impl fmt::Display for RegexError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} (code {:?})", self.error, self.kind)
}
}
impl Regex {
#[must_use]
pub fn regerror(&self, result: ErrorInt) -> RegexError {
let Some(compiled_reg_obj) = self.get() else {
return RegexError::new(
ErrorKind::Binding(BindingErrorCode::REGEX_VACANT),
"Attempted to unwrap a vacant Regex object",
);
};
let bufsize = unsafe { tre::tre_regerror(result, compiled_reg_obj, null_mut(), 0) };
let mut errbuf = vec![0u8; bufsize];
unsafe {
tre::tre_regerror(
result,
compiled_reg_obj,
errbuf.as_mut_ptr().cast::<c_char>(),
bufsize,
);
}
let errstr = CString::from_vec_with_nul(errbuf).map_err(|e| {
RegexError::new(
ErrorKind::Binding(BindingErrorCode::CSTRING),
&format!("Could not convert error buffer to C string: {e}"),
)
});
let Ok(errstr) = errstr else {
return errstr.unwrap_err();
};
let errstr = errstr.to_str().map_err(|e| {
RegexError::new(
ErrorKind::Binding(BindingErrorCode::ENCODING),
&format!("Could not encode error string to UTF-8: {e}"),
)
});
let Ok(errstr) = errstr else {
return errstr.unwrap_err();
};
#[allow(clippy::cast_sign_loss)]
RegexError::new(ErrorKind::Tre(tre::reg_errcode_t(result as c_uint)), errstr)
}
}
#[must_use]
pub fn regerror(compiled_reg: &Regex, result: ErrorInt) -> RegexError {
compiled_reg.regerror(result)
}