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