1use crate::{hybrid::id::LazyStateIDError, nfa, util::search::Anchored};
23/// 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)]
23pub struct BuildError {
24 kind: BuildErrorKind,
25}
2627#[derive(Clone, Debug)]
28enum BuildErrorKind {
29 NFA(nfa::thompson::BuildError),
30 InsufficientCacheCapacity { minimum: usize, given: usize },
31 InsufficientStateIDCapacity { err: LazyStateIDError },
32 Unsupported(&'static str),
33}
3435impl BuildError {
36pub(crate) fn nfa(err: nfa::thompson::BuildError) -> BuildError {
37 BuildError { kind: BuildErrorKind::NFA(err) }
38 }
3940pub(crate) fn insufficient_cache_capacity(
41 minimum: usize,
42 given: usize,
43 ) -> BuildError {
44 BuildError {
45 kind: BuildErrorKind::InsufficientCacheCapacity { minimum, given },
46 }
47 }
4849pub(crate) fn insufficient_state_id_capacity(
50 err: LazyStateIDError,
51 ) -> BuildError {
52 BuildError {
53 kind: BuildErrorKind::InsufficientStateIDCapacity { err },
54 }
55 }
5657pub(crate) fn unsupported_dfa_word_boundary_unicode() -> BuildError {
58let 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}
6566#[cfg(feature = "std")]
67impl std::error::Error for BuildError {
68fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
69match self.kind {
70 BuildErrorKind::NFA(ref err) => Some(err),
71_ => None,
72 }
73 }
74}
7576impl core::fmt::Display for BuildError {
77fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78match self.kind {
79 BuildErrorKind::NFA(_) => write!(f, "error building NFA"),
80 BuildErrorKind::InsufficientCacheCapacity { minimum, given } => {
81write!(
82 f,
83"given cache capacity ({given}) is smaller than \
84 minimum required ({minimum})",
85 )
86 }
87 BuildErrorKind::InsufficientStateIDCapacity { ref err } => {
88 err.fmt(f)
89 }
90 BuildErrorKind::Unsupported(ref msg) => {
91write!(f, "unsupported regex feature for DFAs: {msg}")
92 }
93 }
94 }
95}
9697/// An error that can occur when computing the start state for a search.
98///
99/// Computing a start state can fail for a few reasons, either
100/// based on incorrect configuration or even based on whether
101/// the look-behind byte triggers a quit state. Typically
102/// one does not need to handle this error if you're using
103/// [`DFA::start_state_forward`](crate::hybrid::dfa::DFA::start_state_forward)
104/// (or its reverse counterpart), as that routine automatically converts
105/// `StartError` to a [`MatchError`](crate::MatchError) for you.
106///
107/// This error may be returned by the
108/// [`DFA::start_state`](crate::hybrid::dfa::DFA::start_state) routine.
109///
110/// This error implements the `std::error::Error` trait when the `std` feature
111/// is enabled.
112///
113/// This error is marked as non-exhaustive. New variants may be added in a
114/// semver compatible release.
115#[non_exhaustive]
116#[derive(Clone, Debug)]
117pub enum StartError {
118/// An error that occurs when cache inefficiency has dropped below the
119 /// configured heuristic thresholds.
120Cache {
121/// The underlying cache error that occurred.
122err: CacheError,
123 },
124/// An error that occurs when a starting configuration's look-behind byte
125 /// is in this DFA's quit set.
126Quit {
127/// The quit byte that was found.
128byte: u8,
129 },
130/// An error that occurs when the caller requests an anchored mode that
131 /// isn't supported by the DFA.
132UnsupportedAnchored {
133/// The anchored mode given that is unsupported.
134mode: Anchored,
135 },
136}
137138impl StartError {
139pub(crate) fn cache(err: CacheError) -> StartError {
140 StartError::Cache { err }
141 }
142143pub(crate) fn quit(byte: u8) -> StartError {
144 StartError::Quit { byte }
145 }
146147pub(crate) fn unsupported_anchored(mode: Anchored) -> StartError {
148 StartError::UnsupportedAnchored { mode }
149 }
150}
151152#[cfg(feature = "std")]
153impl std::error::Error for StartError {
154fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
155match *self {
156 StartError::Cache { ref err } => Some(err),
157_ => None,
158 }
159 }
160}
161162impl core::fmt::Display for StartError {
163fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164match *self {
165 StartError::Cache { .. } => write!(
166 f,
167"error computing start state because of cache inefficiency"
168),
169 StartError::Quit { byte } => write!(
170 f,
171"error computing start state because the look-behind byte \
172 {:?} triggered a quit state",
173crate::util::escape::DebugByte(byte),
174 ),
175 StartError::UnsupportedAnchored { mode: Anchored::Yes } => {
176write!(
177 f,
178"error computing start state because \
179 anchored searches are not supported or enabled"
180)
181 }
182 StartError::UnsupportedAnchored { mode: Anchored::No } => {
183write!(
184 f,
185"error computing start state because \
186 unanchored searches are not supported or enabled"
187)
188 }
189 StartError::UnsupportedAnchored {
190 mode: Anchored::Pattern(pid),
191 } => {
192write!(
193 f,
194"error computing start state because \
195 anchored searches for a specific pattern ({}) \
196 are not supported or enabled",
197 pid.as_usize(),
198 )
199 }
200 }
201 }
202}
203204/// An error that occurs when cache usage has become inefficient.
205///
206/// One of the weaknesses of a lazy DFA is that it may need to clear its
207/// cache repeatedly if it's not big enough. If this happens too much, then it
208/// can slow searching down significantly. A mitigation to this is to use
209/// heuristics to detect whether the cache is being used efficiently or not.
210/// If not, then a lazy DFA can return a `CacheError`.
211///
212/// The default configuration of a lazy DFA in this crate is
213/// set such that a `CacheError` will never occur. Instead,
214/// callers must opt into this behavior with settings like
215/// [`dfa::Config::minimum_cache_clear_count`](crate::hybrid::dfa::Config::minimum_cache_clear_count)
216/// and
217/// [`dfa::Config::minimum_bytes_per_state`](crate::hybrid::dfa::Config::minimum_bytes_per_state).
218///
219/// When the `std` feature is enabled, this implements the `std::error::Error`
220/// trait.
221#[derive(Clone, Debug)]
222pub struct CacheError(());
223224impl CacheError {
225pub(crate) fn too_many_cache_clears() -> CacheError {
226 CacheError(())
227 }
228229pub(crate) fn bad_efficiency() -> CacheError {
230 CacheError(())
231 }
232}
233234#[cfg(feature = "std")]
235impl std::error::Error for CacheError {}
236237impl core::fmt::Display for CacheError {
238fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
239write!(f, "lazy DFA cache has been cleared too many times")
240 }
241}