Skip to main content

fancy_regex/
error.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use core::fmt;
4use regex_automata::meta::BuildError as RaBuildError;
5
6/// Result type for this crate with specific error enum.
7pub type Result<T> = ::core::result::Result<T, Error>;
8
9pub type ParseErrorPosition = usize;
10
11/// An error as the result of parsing, compiling or running a regex.
12#[derive(Clone, Debug)]
13#[non_exhaustive]
14pub enum Error {
15    /// An error as a result of parsing a regex pattern, with the position where the error occurred
16    ParseError(ParseErrorPosition, ParseError),
17    /// An error as a result of compiling a regex
18    CompileError(Box<CompileError>),
19    /// An error as a result of running a regex
20    RuntimeError(RuntimeError),
21}
22
23/// An error for the result of parsing a regex pattern.
24#[derive(Clone, Debug)]
25#[non_exhaustive]
26pub enum ParseError {
27    /// General parsing error
28    GeneralParseError(String),
29    /// Opening parenthesis without closing parenthesis, e.g. `(a|b`
30    UnclosedOpenParen,
31    /// Invalid repeat syntax
32    InvalidRepeat,
33    /// Pattern too deeply nested
34    RecursionExceeded,
35    /// Backslash without following character
36    TrailingBackslash,
37    /// Invalid escape
38    InvalidEscape(String),
39    /// Unicode escape not closed
40    UnclosedUnicodeName,
41    /// Invalid hex escape
42    InvalidHex,
43    /// Invalid codepoint for hex or unicode escape
44    InvalidCodepointValue,
45    /// Invalid character class
46    InvalidClass,
47    /// Unknown group flag
48    UnknownFlag(String),
49    /// Changing Unicode inside a pattern not supported
50    ChangingUnicodeModeUnsupported,
51    /// Invalid back reference
52    InvalidBackref,
53    /// Quantifier on lookaround or other zero-width assertion
54    TargetNotRepeatable,
55    /// Couldn't parse group name
56    InvalidGroupName,
57    /// Invalid group id in escape sequence
58    InvalidGroupNameBackref(String),
59}
60
61/// An error as the result of compiling a regex.
62#[derive(Clone, Debug)]
63#[non_exhaustive]
64pub enum CompileError {
65    /// Regex crate error
66    InnerError(RaBuildError),
67    /// Look-behind assertion without constant size
68    LookBehindNotConst,
69    /// Variable-length lookbehind requires feature flag
70    VariableLookBehindRequiresFeature,
71    /// Error building reverse DFA (pattern, error)
72    DfaBuildError(String, String),
73    /// Couldn't parse group name
74    InvalidGroupName,
75    /// Invalid group id in escape sequence
76    InvalidGroupNameBackref(String),
77    /// Invalid back reference
78    InvalidBackref(usize),
79    /// Once named groups are used you cannot refer to groups by number
80    NamedBackrefOnly,
81    /// Feature not supported yet
82    FeatureNotYetSupported(String),
83    /// Subroutine call to non-existent group
84    SubroutineCallTargetNotFound(String, usize),
85    /// Left-recursive subroutine call detected
86    LeftRecursiveSubroutineCall(String),
87    /// Unbounded recursive subroutine call detected
88    NeverEndingRecursion,
89    /// The compiled program exceeded the configured maximum size. Subroutine
90    /// calls are inlined at compile time, so a self- or mutually-recursive
91    /// pattern can expand without bound even when the recursion-depth cap is
92    /// respected; this bounds the emitted VM instruction vector instead.
93    PatternTooComplex,
94    /// Unexpected general error
95    UnexpectedGeneralError(String),
96    /// The pattern combined with the active options can never produce a match. For example,
97    /// setting `find_not_empty` on a pattern whose analysis shows it can only ever produce
98    /// zero-length matches.
99    PatternCanNeverMatch,
100    /// An unresolved AST node was encountered during analysis (internal error)
101    UnresolvedAstNode(usize, String),
102}
103
104/// An error as the result of executing a regex.
105#[derive(Clone, Debug)]
106#[non_exhaustive]
107pub enum RuntimeError {
108    /// Max stack size exceeded for backtracking while executing regex.
109    StackOverflow,
110    /// Max limit for backtracking count exceeded while executing the regex.
111    /// Configure using
112    /// [`RegexBuilder::backtrack_limit`](struct.RegexBuilder.html#method.backtrack_limit).
113    BacktrackLimitExceeded,
114}
115
116#[cfg(feature = "std")]
117impl ::std::error::Error for Error {}
118
119impl fmt::Display for ParseError {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        match self {
122            ParseError::GeneralParseError(s) => write!(f, "General parsing error: {}", s),
123            ParseError::UnclosedOpenParen => {
124                write!(f, "Opening parenthesis without closing parenthesis")
125            }
126            ParseError::InvalidRepeat => write!(f, "Invalid repeat syntax"),
127            ParseError::RecursionExceeded => write!(f, "Pattern too deeply nested"),
128            ParseError::TrailingBackslash => write!(f, "Backslash without following character"),
129            ParseError::InvalidEscape(s) => write!(f, "Invalid escape: {}", s),
130            ParseError::UnclosedUnicodeName => write!(f, "Unicode escape not closed"),
131            ParseError::InvalidHex => write!(f, "Invalid hex escape"),
132            ParseError::InvalidCodepointValue => {
133                write!(f, "Invalid codepoint for hex or unicode escape")
134            }
135            ParseError::InvalidClass => write!(f, "Invalid character class"),
136            ParseError::UnknownFlag(s) => write!(f, "Unknown group flag: {}", s),
137            ParseError::ChangingUnicodeModeUnsupported => {
138                write!(f, "Changing Unicode mode inline is not supported")
139            }
140            ParseError::InvalidBackref => write!(f, "Invalid back reference"),
141            ParseError::InvalidGroupName => write!(f, "Could not parse group name"),
142            ParseError::InvalidGroupNameBackref(s) => {
143                write!(f, "Invalid group name in back reference: {}", s)
144            }
145            ParseError::TargetNotRepeatable => write!(f, "Target of repeat operator is invalid"),
146        }
147    }
148}
149
150impl fmt::Display for CompileError {
151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152        match self {
153            CompileError::InnerError(e) => write!(f, "Regex error: {}", e),
154            CompileError::LookBehindNotConst => {
155                write!(f, "Look-behind assertion without constant size")
156            },
157            CompileError::VariableLookBehindRequiresFeature => {
158                write!(f, "Variable-length lookbehind requires the 'variable-lookbehinds' feature")
159            },
160            CompileError::DfaBuildError(pattern, e) => {
161                write!(f, "Failed to build DFA for {}: {}", pattern, e)
162            },
163            CompileError::InvalidGroupName => write!(f, "Could not parse group name"),
164            CompileError::InvalidGroupNameBackref(s) => write!(f, "Invalid group name in back reference: {}", s),
165            CompileError::InvalidBackref(g) => write!(f, "Invalid back reference to group {}", g),
166            CompileError::NamedBackrefOnly => write!(f, "Numbered backref/call not allowed because named group was used, use a named backref instead"),
167            CompileError::FeatureNotYetSupported(s) => write!(f, "Regex uses currently unimplemented feature: {}", s),
168            CompileError::SubroutineCallTargetNotFound(s, ix) => {
169                write!(f, "Subroutine call target not found at position {}: {}", ix, s)
170            }
171            CompileError::LeftRecursiveSubroutineCall(s) => {
172                write!(f, "Left-recursive subroutine call detected: {}", s)
173            }
174            CompileError::NeverEndingRecursion => {
175                write!(f, "Never-ending recursive subroutine call detected")
176            }
177            CompileError::PatternTooComplex => {
178                write!(
179                    f,
180                    "Pattern is too complex: it exceeded the configured maximum number of VM \
181                     instructions. Try increasing the `max_prog_size` limit or simplifying the \
182                     pattern."
183                )
184            }
185            CompileError::UnexpectedGeneralError(s) => {
186                write!(f, "Unexpected general compilation error: {}", s)
187            }
188            CompileError::PatternCanNeverMatch => {
189                write!(f, "Pattern can never match: the regex is zero-width only but find_not_empty is set")
190            }
191            CompileError::UnresolvedAstNode(ix, node) => {
192                write!(f, "Unresolved AST node encountered during analysis at position {}: {}", ix, node)
193            }
194        }
195    }
196}
197
198impl fmt::Display for RuntimeError {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        match self {
201            RuntimeError::StackOverflow => write!(f, "Max stack size exceeded for backtracking"),
202            RuntimeError::BacktrackLimitExceeded => {
203                write!(f, "Max limit for backtracking count exceeded")
204            }
205        }
206    }
207}
208
209impl fmt::Display for Error {
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        match self {
212            Error::ParseError(position, parse_error) => {
213                write!(f, "Parsing error at position {}: {}", position, parse_error)
214            }
215            Error::CompileError(compile_error) => {
216                write!(f, "Error compiling regex: {}", compile_error)
217            }
218            Error::RuntimeError(runtime_error) => {
219                write!(f, "Error executing regex: {}", runtime_error)
220            }
221        }
222    }
223}
224
225impl From<CompileError> for Error {
226    fn from(compile_error: CompileError) -> Self {
227        Error::CompileError(Box::new(compile_error))
228    }
229}