1use crate::util::{
2 captures, look,
3 primitives::{PatternID, StateID},
4};
5
6/// An error that can occurred during the construction of a thompson NFA.
7///
8/// This error does not provide many introspection capabilities. There are
9/// generally only two things you can do with it:
10///
11/// * Obtain a human readable message via its `std::fmt::Display` impl.
12/// * Access an underlying [`regex_syntax::Error`] type from its `source`
13/// method via the `std::error::Error` trait. This error only occurs when using
14/// convenience routines for building an NFA directly from a pattern string.
15///
16/// Otherwise, errors typically occur when a limit has been breeched. For
17/// example, if the total heap usage of the compiled NFA exceeds the limit
18/// set by [`Config::nfa_size_limit`](crate::nfa::thompson::Config), then
19/// building the NFA will fail.
20#[derive(Clone, Debug)]
21pub struct BuildError {
22 kind: BuildErrorKind,
23}
24
25/// The kind of error that occurred during the construction of a thompson NFA.
26#[derive(Clone, Debug)]
27enum BuildErrorKind {
28 /// An error that occurred while parsing a regular expression. Note that
29 /// this error may be printed over multiple lines, and is generally
30 /// intended to be end user readable on its own.
31 #[cfg(feature = "syntax")]
32 Syntax(regex_syntax::Error),
33 /// An error that occurs if the capturing groups provided to an NFA builder
34 /// do not satisfy the documented invariants. For example, things like
35 /// too many groups, missing groups, having the first (zeroth) group be
36 /// named or duplicate group names within the same pattern.
37 Captures(captures::GroupInfoError),
38 /// An error that occurs when an NFA contains a Unicode word boundary, but
39 /// where the crate was compiled without the necessary data for dealing
40 /// with Unicode word boundaries.
41 Word(look::UnicodeWordBoundaryError),
42 /// An error that occurs if too many patterns were given to the NFA
43 /// compiler.
44 TooManyPatterns {
45 /// The number of patterns given, which exceeds the limit.
46 given: usize,
47 /// The limit on the number of patterns.
48 limit: usize,
49 },
50 /// An error that occurs if too states are produced while building an NFA.
51 TooManyStates {
52 /// The minimum number of states that are desired, which exceeds the
53 /// limit.
54 given: usize,
55 /// The limit on the number of states.
56 limit: usize,
57 },
58 /// An error that occurs when NFA compilation exceeds a configured heap
59 /// limit.
60 ExceededSizeLimit {
61 /// The configured limit, in bytes.
62 limit: usize,
63 },
64 /// An error that occurs when an invalid capture group index is added to
65 /// the NFA. An "invalid" index can be one that would otherwise overflow
66 /// a `usize` on the current target.
67 InvalidCaptureIndex {
68 /// The invalid index that was given.
69 index: u32,
70 },
71 /// An error that occurs when one tries to build a reverse NFA with
72 /// captures enabled. Currently, this isn't supported, but we probably
73 /// should support it at some point.
74 #[cfg(feature = "syntax")]
75 UnsupportedCaptures,
76}
77
78impl BuildError {
79 /// If this error occurred because the NFA exceeded the configured size
80 /// limit before being built, then this returns the configured size limit.
81 ///
82 /// The limit returned is what was configured, and corresponds to the
83 /// maximum amount of heap usage in bytes.
84 pub fn size_limit(&self) -> Option<usize> {
85 match self.kind {
86 BuildErrorKind::ExceededSizeLimit { limit } => Some(limit),
87 _ => None,
88 }
89 }
90
91 fn kind(&self) -> &BuildErrorKind {
92 &self.kind
93 }
94
95 #[cfg(feature = "syntax")]
96 pub(crate) fn syntax(err: regex_syntax::Error) -> BuildError {
97 BuildError { kind: BuildErrorKind::Syntax(err) }
98 }
99
100 pub(crate) fn captures(err: captures::GroupInfoError) -> BuildError {
101 BuildError { kind: BuildErrorKind::Captures(err) }
102 }
103
104 pub(crate) fn word(err: look::UnicodeWordBoundaryError) -> BuildError {
105 BuildError { kind: BuildErrorKind::Word(err) }
106 }
107
108 pub(crate) fn too_many_patterns(given: usize) -> BuildError {
109 let limit = PatternID::LIMIT;
110 BuildError { kind: BuildErrorKind::TooManyPatterns { given, limit } }
111 }
112
113 pub(crate) fn too_many_states(given: usize) -> BuildError {
114 let limit = StateID::LIMIT;
115 BuildError { kind: BuildErrorKind::TooManyStates { given, limit } }
116 }
117
118 pub(crate) fn exceeded_size_limit(limit: usize) -> BuildError {
119 BuildError { kind: BuildErrorKind::ExceededSizeLimit { limit } }
120 }
121
122 pub(crate) fn invalid_capture_index(index: u32) -> BuildError {
123 BuildError { kind: BuildErrorKind::InvalidCaptureIndex { index } }
124 }
125
126 #[cfg(feature = "syntax")]
127 pub(crate) fn unsupported_captures() -> BuildError {
128 BuildError { kind: BuildErrorKind::UnsupportedCaptures }
129 }
130}
131
132#[cfg(feature = "std")]
133impl std::error::Error for BuildError {
134 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
135 match self.kind() {
136 #[cfg(feature = "syntax")]
137 BuildErrorKind::Syntax(ref err) => Some(err),
138 BuildErrorKind::Captures(ref err) => Some(err),
139 _ => None,
140 }
141 }
142}
143
144impl core::fmt::Display for BuildError {
145 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146 match self.kind() {
147 #[cfg(feature = "syntax")]
148 BuildErrorKind::Syntax(_) => write!(f, "error parsing regex"),
149 BuildErrorKind::Captures(_) => {
150 write!(f, "error with capture groups")
151 }
152 BuildErrorKind::Word(_) => {
153 write!(f, "NFA contains Unicode word boundary")
154 }
155 BuildErrorKind::TooManyPatterns { given, limit } => write!(
156 f,
157 "attempted to compile {} patterns, \
158 which exceeds the limit of {}",
159 given, limit,
160 ),
161 BuildErrorKind::TooManyStates { given, limit } => write!(
162 f,
163 "attempted to compile {} NFA states, \
164 which exceeds the limit of {}",
165 given, limit,
166 ),
167 BuildErrorKind::ExceededSizeLimit { limit } => write!(
168 f,
169 "heap usage during NFA compilation exceeded limit of {}",
170 limit,
171 ),
172 BuildErrorKind::InvalidCaptureIndex { index } => write!(
173 f,
174 "capture group index {} is invalid (too big or discontinuous)",
175 index,
176 ),
177 #[cfg(feature = "syntax")]
178 BuildErrorKind::UnsupportedCaptures => write!(
179 f,
180 "currently captures must be disabled when compiling \
181 a reverse NFA",
182 ),
183 }
184 }
185}
186