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}