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