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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use std::error;
use std::fmt;

use libc::c_int;
use pcre2_sys::*;

/// 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)]
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,
    /// Hints that destructuring should not be exhaustive.
    ///
    /// This enum may grow additional variants, so this makes sure clients
    /// don't count on exhaustive matching. (Otherwise, adding a new variant
    /// could break existing code.)
    #[doc(hidden)]
    __Nonexhaustive,
}

impl Error {
    /// Create a new compilation error.
    pub(crate) fn compile(code: c_int, offset: usize) -> Error {
        Error {
            kind: ErrorKind::Compile,
            code: code,
            offset: Some(offset),
        }
    }

    /// Create a new JIT compilation error.
    pub(crate) fn jit(code: c_int) -> Error {
        Error {
            kind: ErrorKind::JIT,
            code: code,
            offset: None,
        }
    }

    /// Create a new matching error.
    pub(crate) fn matching(code: c_int) -> Error {
        Error {
            kind: ErrorKind::Match,
            code: code,
            offset: None,
        }
    }

    /// Create a new info error.
    pub(crate) fn info(code: c_int) -> Error {
        Error {
            kind: ErrorKind::Info,
            code: code,
            offset: None,
        }
    }

    /// Create a new option error.
    pub(crate) fn option(code: c_int) -> Error {
        Error {
            kind: ErrorKind::Option,
            code: 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 error::Error for Error {
    fn description(&self) -> &str {
        "pcre2 error"
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> 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)
            }
            _ => unreachable!(),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> 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()
    }
}