tre_regex/
err.rs

1use std::ffi::{c_char, c_int, c_uint, CString};
2use std::fmt;
3use std::ptr::null_mut;
4
5use crate::{tre, Regex};
6
7// Public types
8pub type ErrorInt = c_int;
9pub type Result<T> = std::result::Result<T, RegexError>;
10
11/// Custom error type for errors in the binding itself.
12#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
13pub struct BindingErrorCode(u32);
14
15impl BindingErrorCode {
16    /// Error occured with [`CString`]
17    pub const CSTRING: Self = Self(1);
18
19    /// Error occured with encoding bytes
20    pub const ENCODING: Self = Self(2);
21
22    /// An attempt was made to unwrap a vacant [`Regex`] object
23    pub const REGEX_VACANT: Self = Self(3);
24}
25
26/// Type of error: `Binding` (see [`BindingErrorCode`]), or `Tre`
27///
28/// See the TRE documentation for more information on valid error codes for `Tre`.
29#[derive(Debug, PartialEq, Eq)]
30pub enum ErrorKind {
31    /// Binding-specific error
32    Binding(BindingErrorCode),
33
34    /// Error from TRE
35    Tre(tre::reg_errcode_t),
36}
37
38/// Error type returned in results
39#[derive(Debug, PartialEq, Eq)]
40pub struct RegexError {
41    /// Kind of error
42    pub kind: ErrorKind,
43
44    /// Error string
45    pub error: String,
46}
47
48impl RegexError {
49    #[must_use]
50    #[inline]
51    pub fn new(kind: ErrorKind, error: &str) -> Self {
52        Self {
53            kind,
54            error: error.to_string(),
55        }
56    }
57}
58
59impl std::error::Error for RegexError {}
60
61// Quick and dirty display impl
62impl fmt::Display for RegexError {
63    #[inline]
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        write!(f, "{} (code {:?})", self.error, self.kind)
66    }
67}
68
69impl Regex {
70    /// Given the [`ErrorInt`] code and this object, build a [`RegexError`].
71    ///
72    /// # Arguments
73    /// * `result`: the TRE result code, see [`reg_errcode_t`](tre_regex_sys::reg_errcode_t).
74    ///
75    /// # Returns
76    /// A [`RegexError`] object. If creating the object fails, there is no way to know for sure.
77    /// Fortunately, this should be a nonexistent occurence if the API is used correctly.
78    #[must_use]
79    pub fn regerror(&self, result: ErrorInt) -> RegexError {
80        // SAFETY: compiled_reg should be valid; see safety concerns for Regex.
81        let Some(compiled_reg_obj) = self.get() else {
82            return RegexError::new(
83                ErrorKind::Binding(BindingErrorCode::REGEX_VACANT),
84                "Attempted to unwrap a vacant Regex object",
85            );
86        };
87        let bufsize = unsafe { tre::tre_regerror(result, compiled_reg_obj, null_mut(), 0) };
88        let mut errbuf = vec![0u8; bufsize];
89        // SAFETY: compiled_reg should be valid; errbuf has enough room as validated above
90        unsafe {
91            tre::tre_regerror(
92                result,
93                compiled_reg_obj,
94                errbuf.as_mut_ptr().cast::<c_char>(),
95                bufsize,
96            );
97        }
98        let errstr = CString::from_vec_with_nul(errbuf).map_err(|e| {
99            RegexError::new(
100                ErrorKind::Binding(BindingErrorCode::CSTRING),
101                &format!("Could not convert error buffer to C string: {e}"),
102            )
103        });
104        let Ok(errstr) = errstr else {
105            return errstr.unwrap_err();
106        };
107        let errstr = errstr.to_str().map_err(|e| {
108            RegexError::new(
109                ErrorKind::Binding(BindingErrorCode::ENCODING),
110                &format!("Could not encode error string to UTF-8: {e}"),
111            )
112        });
113        let Ok(errstr) = errstr else {
114            return errstr.unwrap_err();
115        };
116
117        // Value cannot ever be negative.
118        #[allow(clippy::cast_sign_loss)]
119        RegexError::new(ErrorKind::Tre(tre::reg_errcode_t(result as c_uint)), errstr)
120    }
121}
122
123/// Given a [`Regex`] struct and [`ErrorInt`] code, build a [`RegexError`].
124///
125/// This is a thin wrapper around [`Regex::regerror`].
126///
127/// # Arguments
128/// * `compiled_reg`: the compiled `Regex` that triggered the error.
129/// * `result`: the TRE result code, see [`reg_errcode_t`](tre_regex_sys::reg_errcode_t).
130///
131/// **WARNING**: you should rarely need to call this directly.
132///
133/// # Returns
134/// A [`RegexError`] object. If creating the object fails, there is no way to know for sure.
135/// Fortunately, this should be a nonexistent occurence if the API is used correctly.
136///
137/// # Examples
138/// ```
139/// use std::ffi::c_char;
140/// use std::ptr::null_mut;
141/// use tre_regex::{{tre::{tre_regcomp, regex_t}}, Regex, regerror};
142///
143/// let mut compiled_reg: regex_t = Default::default();
144/// let result = unsafe {
145///     tre_regcomp(&mut compiled_reg, b"[a\0".as_ptr() as *const c_char, 0)
146/// };
147/// let regex_error = regerror(&unsafe { Regex::new_from(compiled_reg) }, result);
148/// println!("Error with regex: {regex_error}");
149/// ```
150#[must_use]
151pub fn regerror(compiled_reg: &Regex, result: ErrorInt) -> RegexError {
152    compiled_reg.regerror(result)
153}