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}