regex_automata/nfa/thompson/
error.rs

1use crate::util::{
2    captures, look,
3    primitives::{PatternID, StateID},
4};
5
6/// An error that can occurred during the construction of a thompson NFA.
7///
8/// This error does not provide many introspection capabilities. There are
9/// generally only two things you can do with it:
10///
11/// * Obtain a human readable message via its `std::fmt::Display` impl.
12/// * Access an underlying [`regex_syntax::Error`] type from its `source`
13/// method via the `std::error::Error` trait. This error only occurs when using
14/// convenience routines for building an NFA directly from a pattern string.
15///
16/// Otherwise, errors typically occur when a limit has been breeched. For
17/// example, if the total heap usage of the compiled NFA exceeds the limit
18/// set by [`Config::nfa_size_limit`](crate::nfa::thompson::Config), then
19/// building the NFA will fail.
20#[derive(Clone, Debug)]
21pub struct BuildError {
22    kind: BuildErrorKind,
23}
24
25/// The kind of error that occurred during the construction of a thompson NFA.
26#[derive(Clone, Debug)]
27enum BuildErrorKind {
28    /// An error that occurred while parsing a regular expression. Note that
29    /// this error may be printed over multiple lines, and is generally
30    /// intended to be end user readable on its own.
31    #[cfg(feature = "syntax")]
32    Syntax(regex_syntax::Error),
33    /// An error that occurs if the capturing groups provided to an NFA builder
34    /// do not satisfy the documented invariants. For example, things like
35    /// too many groups, missing groups, having the first (zeroth) group be
36    /// named or duplicate group names within the same pattern.
37    Captures(captures::GroupInfoError),
38    /// An error that occurs when an NFA contains a Unicode word boundary, but
39    /// where the crate was compiled without the necessary data for dealing
40    /// with Unicode word boundaries.
41    Word(look::UnicodeWordBoundaryError),
42    /// An error that occurs if too many patterns were given to the NFA
43    /// compiler.
44    TooManyPatterns {
45        /// The number of patterns given, which exceeds the limit.
46        given: usize,
47        /// The limit on the number of patterns.
48        limit: usize,
49    },
50    /// An error that occurs if too states are produced while building an NFA.
51    TooManyStates {
52        /// The minimum number of states that are desired, which exceeds the
53        /// limit.
54        given: usize,
55        /// The limit on the number of states.
56        limit: usize,
57    },
58    /// An error that occurs when NFA compilation exceeds a configured heap
59    /// limit.
60    ExceededSizeLimit {
61        /// The configured limit, in bytes.
62        limit: usize,
63    },
64    /// An error that occurs when an invalid capture group index is added to
65    /// the NFA. An "invalid" index can be one that would otherwise overflow
66    /// a `usize` on the current target.
67    InvalidCaptureIndex {
68        /// The invalid index that was given.
69        index: u32,
70    },
71    /// An error that occurs when one tries to build a reverse NFA with
72    /// captures enabled. Currently, this isn't supported, but we probably
73    /// should support it at some point.
74    #[cfg(feature = "syntax")]
75    UnsupportedCaptures,
76}
77
78impl BuildError {
79    /// If this error occurred because the NFA exceeded the configured size
80    /// limit before being built, then this returns the configured size limit.
81    ///
82    /// The limit returned is what was configured, and corresponds to the
83    /// maximum amount of heap usage in bytes.
84    pub fn size_limit(&self) -> Option<usize> {
85        match self.kind {
86            BuildErrorKind::ExceededSizeLimit { limit } => Some(limit),
87            _ => None,
88        }
89    }
90
91    fn kind(&self) -> &BuildErrorKind {
92        &self.kind
93    }
94
95    #[cfg(feature = "syntax")]
96    pub(crate) fn syntax(err: regex_syntax::Error) -> BuildError {
97        BuildError { kind: BuildErrorKind::Syntax(err) }
98    }
99
100    pub(crate) fn captures(err: captures::GroupInfoError) -> BuildError {
101        BuildError { kind: BuildErrorKind::Captures(err) }
102    }
103
104    pub(crate) fn word(err: look::UnicodeWordBoundaryError) -> BuildError {
105        BuildError { kind: BuildErrorKind::Word(err) }
106    }
107
108    pub(crate) fn too_many_patterns(given: usize) -> BuildError {
109        let limit = PatternID::LIMIT;
110        BuildError { kind: BuildErrorKind::TooManyPatterns { given, limit } }
111    }
112
113    pub(crate) fn too_many_states(given: usize) -> BuildError {
114        let limit = StateID::LIMIT;
115        BuildError { kind: BuildErrorKind::TooManyStates { given, limit } }
116    }
117
118    pub(crate) fn exceeded_size_limit(limit: usize) -> BuildError {
119        BuildError { kind: BuildErrorKind::ExceededSizeLimit { limit } }
120    }
121
122    pub(crate) fn invalid_capture_index(index: u32) -> BuildError {
123        BuildError { kind: BuildErrorKind::InvalidCaptureIndex { index } }
124    }
125
126    #[cfg(feature = "syntax")]
127    pub(crate) fn unsupported_captures() -> BuildError {
128        BuildError { kind: BuildErrorKind::UnsupportedCaptures }
129    }
130}
131
132#[cfg(feature = "std")]
133impl std::error::Error for BuildError {
134    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
135        match self.kind() {
136            #[cfg(feature = "syntax")]
137            BuildErrorKind::Syntax(ref err) => Some(err),
138            BuildErrorKind::Captures(ref err) => Some(err),
139            _ => None,
140        }
141    }
142}
143
144impl core::fmt::Display for BuildError {
145    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146        match self.kind() {
147            #[cfg(feature = "syntax")]
148            BuildErrorKind::Syntax(_) => write!(f, "error parsing regex"),
149            BuildErrorKind::Captures(_) => {
150                write!(f, "error with capture groups")
151            }
152            BuildErrorKind::Word(_) => {
153                write!(f, "NFA contains Unicode word boundary")
154            }
155            BuildErrorKind::TooManyPatterns { given, limit } => write!(
156                f,
157                "attempted to compile {} patterns, \
158                 which exceeds the limit of {}",
159                given, limit,
160            ),
161            BuildErrorKind::TooManyStates { given, limit } => write!(
162                f,
163                "attempted to compile {} NFA states, \
164                 which exceeds the limit of {}",
165                given, limit,
166            ),
167            BuildErrorKind::ExceededSizeLimit { limit } => write!(
168                f,
169                "heap usage during NFA compilation exceeded limit of {}",
170                limit,
171            ),
172            BuildErrorKind::InvalidCaptureIndex { index } => write!(
173                f,
174                "capture group index {} is invalid (too big or discontinuous)",
175                index,
176            ),
177            #[cfg(feature = "syntax")]
178            BuildErrorKind::UnsupportedCaptures => write!(
179                f,
180                "currently captures must be disabled when compiling \
181                 a reverse NFA",
182            ),
183        }
184    }
185}