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}