regex_automata/hybrid/
error.rs

1use 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)]
23pub struct BuildError {
24    kind: BuildErrorKind,
25}
26
27#[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}
34
35impl 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")]
67impl 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
76impl 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)]
118pub 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
139impl 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")]
154impl 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
163impl 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)]
223pub struct CacheError(());
224
225impl 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")]
236impl std::error::Error for CacheError {}
237
238impl 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}