1 | use crate::{hybrid::id::LazyStateIDError, nfa, util::search::Anchored}; |
2 | |
3 | /// An error that occurs when initial construction of a lazy DFA fails. |
4 | /// |
5 | /// A build error can occur when insufficient cache capacity is configured or |
6 | /// if something about the NFA is unsupported. (For example, if one attempts |
7 | /// to build a lazy DFA without heuristic Unicode support but with an NFA that |
8 | /// contains a Unicode word boundary.) |
9 | /// |
10 | /// This error does not provide many introspection capabilities. There are |
11 | /// generally only two things you can do with it: |
12 | /// |
13 | /// * Obtain a human readable message via its `std::fmt::Display` impl. |
14 | /// * Access an underlying |
15 | /// [`nfa::thompson::BuildError`](crate::nfa::thompson::BuildError) |
16 | /// type from its `source` method via the `std::error::Error` trait. This error |
17 | /// only occurs when using convenience routines for building a lazy DFA |
18 | /// directly from a pattern string. |
19 | /// |
20 | /// When the `std` feature is enabled, this implements the `std::error::Error` |
21 | /// trait. |
22 | #[derive(Clone, Debug)] |
23 | pub struct BuildError { |
24 | kind: BuildErrorKind, |
25 | } |
26 | |
27 | #[derive(Clone, Debug)] |
28 | enum BuildErrorKind { |
29 | NFA(nfa::thompson::BuildError), |
30 | InsufficientCacheCapacity { minimum: usize, given: usize }, |
31 | InsufficientStateIDCapacity { err: LazyStateIDError }, |
32 | Unsupported(&'static str), |
33 | } |
34 | |
35 | impl BuildError { |
36 | pub(crate) fn nfa(err: nfa::thompson::BuildError) -> BuildError { |
37 | BuildError { kind: BuildErrorKind::NFA(err) } |
38 | } |
39 | |
40 | pub(crate) fn insufficient_cache_capacity( |
41 | minimum: usize, |
42 | given: usize, |
43 | ) -> BuildError { |
44 | BuildError { |
45 | kind: BuildErrorKind::InsufficientCacheCapacity { minimum, given }, |
46 | } |
47 | } |
48 | |
49 | pub(crate) fn insufficient_state_id_capacity( |
50 | err: LazyStateIDError, |
51 | ) -> BuildError { |
52 | BuildError { |
53 | kind: BuildErrorKind::InsufficientStateIDCapacity { err }, |
54 | } |
55 | } |
56 | |
57 | pub(crate) fn unsupported_dfa_word_boundary_unicode() -> BuildError { |
58 | let msg = "cannot build lazy DFAs for regexes with Unicode word \ |
59 | boundaries; switch to ASCII word boundaries, or \ |
60 | heuristically enable Unicode word boundaries or use a \ |
61 | different regex engine" ; |
62 | BuildError { kind: BuildErrorKind::Unsupported(msg) } |
63 | } |
64 | } |
65 | |
66 | #[cfg (feature = "std" )] |
67 | impl std::error::Error for BuildError { |
68 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
69 | match self.kind { |
70 | BuildErrorKind::NFA(ref err) => Some(err), |
71 | _ => None, |
72 | } |
73 | } |
74 | } |
75 | |
76 | impl core::fmt::Display for BuildError { |
77 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
78 | match self.kind { |
79 | BuildErrorKind::NFA(_) => write!(f, "error building NFA" ), |
80 | BuildErrorKind::InsufficientCacheCapacity { minimum, given } => { |
81 | write!( |
82 | f, |
83 | "given cache capacity ({}) is smaller than \ |
84 | minimum required ({})" , |
85 | given, minimum, |
86 | ) |
87 | } |
88 | BuildErrorKind::InsufficientStateIDCapacity { ref err } => { |
89 | err.fmt(f) |
90 | } |
91 | BuildErrorKind::Unsupported(ref msg) => { |
92 | write!(f, "unsupported regex feature for DFAs: {}" , msg) |
93 | } |
94 | } |
95 | } |
96 | } |
97 | |
98 | /// An error that can occur when computing the start state for a search. |
99 | /// |
100 | /// Computing a start state can fail for a few reasons, either |
101 | /// based on incorrect configuration or even based on whether |
102 | /// the look-behind byte triggers a quit state. Typically |
103 | /// one does not need to handle this error if you're using |
104 | /// [`DFA::start_state_forward`](crate::hybrid::dfa::DFA::start_state_forward) |
105 | /// (or its reverse counterpart), as that routine automatically converts |
106 | /// `StartError` to a [`MatchError`](crate::MatchError) for you. |
107 | /// |
108 | /// This error may be returned by the |
109 | /// [`DFA::start_state`](crate::hybrid::dfa::DFA::start_state) routine. |
110 | /// |
111 | /// This error implements the `std::error::Error` trait when the `std` feature |
112 | /// is enabled. |
113 | /// |
114 | /// This error is marked as non-exhaustive. New variants may be added in a |
115 | /// semver compatible release. |
116 | #[non_exhaustive ] |
117 | #[derive(Clone, Debug)] |
118 | pub enum StartError { |
119 | /// An error that occurs when cache inefficiency has dropped below the |
120 | /// configured heuristic thresholds. |
121 | Cache { |
122 | /// The underlying cache error that occurred. |
123 | err: CacheError, |
124 | }, |
125 | /// An error that occurs when a starting configuration's look-behind byte |
126 | /// is in this DFA's quit set. |
127 | Quit { |
128 | /// The quit byte that was found. |
129 | byte: u8, |
130 | }, |
131 | /// An error that occurs when the caller requests an anchored mode that |
132 | /// isn't supported by the DFA. |
133 | UnsupportedAnchored { |
134 | /// The anchored mode given that is unsupported. |
135 | mode: Anchored, |
136 | }, |
137 | } |
138 | |
139 | impl StartError { |
140 | pub(crate) fn cache(err: CacheError) -> StartError { |
141 | StartError::Cache { err } |
142 | } |
143 | |
144 | pub(crate) fn quit(byte: u8) -> StartError { |
145 | StartError::Quit { byte } |
146 | } |
147 | |
148 | pub(crate) fn unsupported_anchored(mode: Anchored) -> StartError { |
149 | StartError::UnsupportedAnchored { mode } |
150 | } |
151 | } |
152 | |
153 | #[cfg (feature = "std" )] |
154 | impl std::error::Error for StartError { |
155 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
156 | match *self { |
157 | StartError::Cache { ref err } => Some(err), |
158 | _ => None, |
159 | } |
160 | } |
161 | } |
162 | |
163 | impl core::fmt::Display for StartError { |
164 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
165 | match *self { |
166 | StartError::Cache { .. } => write!( |
167 | f, |
168 | "error computing start state because of cache inefficiency" |
169 | ), |
170 | StartError::Quit { byte } => write!( |
171 | f, |
172 | "error computing start state because the look-behind byte \ |
173 | {:?} triggered a quit state" , |
174 | crate::util::escape::DebugByte(byte), |
175 | ), |
176 | StartError::UnsupportedAnchored { mode: Anchored::Yes } => { |
177 | write!( |
178 | f, |
179 | "error computing start state because \ |
180 | anchored searches are not supported or enabled" |
181 | ) |
182 | } |
183 | StartError::UnsupportedAnchored { mode: Anchored::No } => { |
184 | write!( |
185 | f, |
186 | "error computing start state because \ |
187 | unanchored searches are not supported or enabled" |
188 | ) |
189 | } |
190 | StartError::UnsupportedAnchored { |
191 | mode: Anchored::Pattern(pid), |
192 | } => { |
193 | write!( |
194 | f, |
195 | "error computing start state because \ |
196 | anchored searches for a specific pattern ({}) \ |
197 | are not supported or enabled" , |
198 | pid.as_usize(), |
199 | ) |
200 | } |
201 | } |
202 | } |
203 | } |
204 | |
205 | /// An error that occurs when cache usage has become inefficient. |
206 | /// |
207 | /// One of the weaknesses of a lazy DFA is that it may need to clear its |
208 | /// cache repeatedly if it's not big enough. If this happens too much, then it |
209 | /// can slow searching down significantly. A mitigation to this is to use |
210 | /// heuristics to detect whether the cache is being used efficiently or not. |
211 | /// If not, then a lazy DFA can return a `CacheError`. |
212 | /// |
213 | /// The default configuration of a lazy DFA in this crate is |
214 | /// set such that a `CacheError` will never occur. Instead, |
215 | /// callers must opt into this behavior with settings like |
216 | /// [`dfa::Config::minimum_cache_clear_count`](crate::hybrid::dfa::Config::minimum_cache_clear_count) |
217 | /// and |
218 | /// [`dfa::Config::minimum_bytes_per_state`](crate::hybrid::dfa::Config::minimum_bytes_per_state). |
219 | /// |
220 | /// When the `std` feature is enabled, this implements the `std::error::Error` |
221 | /// trait. |
222 | #[derive(Clone, Debug)] |
223 | pub struct CacheError(()); |
224 | |
225 | impl CacheError { |
226 | pub(crate) fn too_many_cache_clears() -> CacheError { |
227 | CacheError(()) |
228 | } |
229 | |
230 | pub(crate) fn bad_efficiency() -> CacheError { |
231 | CacheError(()) |
232 | } |
233 | } |
234 | |
235 | #[cfg (feature = "std" )] |
236 | impl std::error::Error for CacheError {} |
237 | |
238 | impl core::fmt::Display for CacheError { |
239 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
240 | write!(f, "lazy DFA cache has been cleared too many times" ) |
241 | } |
242 | } |
243 | |