Skip to main content

tre_regex/
err.rs

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