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 {
nfa(err: nfa::thompson::BuildError) -> BuildError36     pub(crate) fn nfa(err: nfa::thompson::BuildError) -> BuildError {
37         BuildError { kind: BuildErrorKind::NFA(err) }
38     }
39 
insufficient_cache_capacity( minimum: usize, given: usize, ) -> BuildError40     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 
insufficient_state_id_capacity( err: LazyStateIDError, ) -> BuildError49     pub(crate) fn insufficient_state_id_capacity(
50         err: LazyStateIDError,
51     ) -> BuildError {
52         BuildError {
53             kind: BuildErrorKind::InsufficientStateIDCapacity { err },
54         }
55     }
56 
unsupported_dfa_word_boundary_unicode() -> BuildError57     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 {
source(&self) -> Option<&(dyn std::error::Error + 'static)>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 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result77     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 {
cache(err: CacheError) -> StartError140     pub(crate) fn cache(err: CacheError) -> StartError {
141         StartError::Cache { err }
142     }
143 
quit(byte: u8) -> StartError144     pub(crate) fn quit(byte: u8) -> StartError {
145         StartError::Quit { byte }
146     }
147 
unsupported_anchored(mode: Anchored) -> StartError148     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 {
source(&self) -> Option<&(dyn std::error::Error + 'static)>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 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result164     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 {
too_many_cache_clears() -> CacheError226     pub(crate) fn too_many_cache_clears() -> CacheError {
227         CacheError(())
228     }
229 
bad_efficiency() -> CacheError230     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 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result239     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