pcre2/
error.rs

1use {
2    libc::c_int,
3    pcre2_sys::{
4        PCRE2_ERROR_BADDATA, PCRE2_ERROR_NOMEMORY, pcre2_get_error_message_8,
5    },
6};
7
8/// A PCRE2 error.
9///
10/// An error can occur during compilation or during matching. The kind of this
11/// error indicates the type of operation being performed when the error
12/// occurred.
13#[derive(Clone)]
14pub struct Error {
15    kind: ErrorKind,
16    code: c_int,
17    offset: Option<usize>,
18}
19
20/// The kind of an error indicates the type of operation that was attempted
21/// that resulted in an error.
22///
23/// This enum may expand over time.
24#[derive(Clone, Debug)]
25#[non_exhaustive]
26pub enum ErrorKind {
27    /// An error occurred during compilation of a regex.
28    Compile,
29    /// An error occurred during JIT compilation of a regex.
30    JIT,
31    /// An error occurred while matching.
32    Match,
33    /// An error occurred while querying a compiled regex for info.
34    Info,
35    /// An error occurred while setting an option.
36    Option,
37}
38
39impl Error {
40    /// Create a new compilation error.
41    pub(crate) fn compile(code: c_int, offset: usize) -> Error {
42        Error { kind: ErrorKind::Compile, code, offset: Some(offset) }
43    }
44
45    /// Create a new JIT compilation error.
46    pub(crate) fn jit(code: c_int) -> Error {
47        Error { kind: ErrorKind::JIT, code, offset: None }
48    }
49
50    /// Create a new matching error.
51    pub(crate) fn matching(code: c_int) -> Error {
52        Error { kind: ErrorKind::Match, code, offset: None }
53    }
54
55    /// Create a new info error.
56    pub(crate) fn info(code: c_int) -> Error {
57        Error { kind: ErrorKind::Info, code, offset: None }
58    }
59
60    /// Create a new option error.
61    pub(crate) fn option(code: c_int) -> Error {
62        Error { kind: ErrorKind::Option, code, offset: None }
63    }
64
65    /// Return the kind of this error.
66    ///
67    /// The kind indicates the type of operation that was attempted which
68    /// resulted in this error.
69    pub fn kind(&self) -> &ErrorKind {
70        &self.kind
71    }
72
73    /// Return the raw underlying PCRE2 error code.
74    ///
75    /// This can be useful if one needs to determine exactly which error
76    /// occurred, which can be done with case analysis over the constants
77    /// exported in the `pcre2-sys` crate.
78    pub fn code(&self) -> c_int {
79        self.code
80    }
81
82    /// Return the underlying offset associated with this error, if one exists.
83    ///
84    /// The offset is typically only available for compile time errors, and
85    /// is supposed to indicate the general position in the pattern where an
86    /// error occurred.
87    pub fn offset(&self) -> Option<usize> {
88        self.offset
89    }
90
91    /// Returns the error message from PCRE2.
92    fn error_message(&self) -> String {
93        // PCRE2 docs say a buffer size of 120 bytes is enough, but we're
94        // cautious and double it.
95        let mut buf = [0u8; 240];
96        let rc = unsafe {
97            pcre2_get_error_message_8(self.code, buf.as_mut_ptr(), buf.len())
98        };
99        // Errors are only ever constructed from codes reported by PCRE2, so
100        // our code should always be valid.
101        assert!(rc != PCRE2_ERROR_BADDATA, "used an invalid error code");
102        // PCRE2 docs claim 120 bytes is enough, and we use more, so...
103        assert!(rc != PCRE2_ERROR_NOMEMORY, "buffer size too small");
104        // Sanity check that we do indeed have a non-negative result. 0 is OK.
105        assert!(rc >= 0, "expected non-negative but got {}", rc);
106        String::from_utf8(buf[..rc as usize].to_vec()).expect("valid UTF-8")
107    }
108}
109
110impl std::error::Error for Error {
111    fn description(&self) -> &str {
112        "pcre2 error"
113    }
114}
115
116impl std::fmt::Display for Error {
117    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
118        let msg = self.error_message();
119        match self.kind {
120            ErrorKind::Compile => match self.offset {
121                None => {
122                    write!(f, "PCRE2: error compiling pattern: {}", msg)
123                }
124                Some(offset) => {
125                    write!(
126                        f,
127                        "PCRE2: error compiling pattern at offset {}: {}",
128                        offset, msg
129                    )
130                }
131            },
132            ErrorKind::JIT => {
133                write!(f, "PCRE2: error JIT compiling pattern: {}", msg)
134            }
135            ErrorKind::Match => {
136                write!(f, "PCRE2: error matching: {}", msg)
137            }
138            ErrorKind::Info => {
139                write!(f, "PCRE2: error getting info: {}", msg)
140            }
141            ErrorKind::Option => {
142                write!(f, "PCRE2: error setting option: {}", msg)
143            }
144        }
145    }
146}
147
148impl std::fmt::Debug for Error {
149    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
150        // We include the error message in the debug representation since
151        // most humans probably don't have PCRE2 error codes memorized.
152        f.debug_struct("Error")
153            .field("kind", &self.kind)
154            .field("code", &self.code)
155            .field("offset", &self.offset)
156            .field("message", &self.error_message())
157            .finish()
158    }
159}