Skip to content

Commit 225c94c

Browse files
committed
automata/dfa: add BuildError::is_size_limit_exceeded
This adds a new predicate that supports very minimal introspection ability into why DFA construction failed. Closes #1236
1 parent 9870c06 commit 225c94c

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

regex-automata/src/dfa/dense.rs

+68
Original file line numberDiff line numberDiff line change
@@ -4958,6 +4958,74 @@ pub struct BuildError {
49584958
kind: BuildErrorKind,
49594959
}
49604960

4961+
#[cfg(feature = "dfa-build")]
4962+
impl BuildError {
4963+
/// Returns true if and only if this error corresponds to an error with DFA
4964+
/// construction that occurred because of exceeding a size limit.
4965+
///
4966+
/// While this can occur when size limits like [`Config::dfa_size_limit`]
4967+
/// or [`Config::determinize_size_limit`] are exceeded, this can also occur
4968+
/// when the number of states or patterns exceeds a hard-coded maximum.
4969+
/// (Where these maximums are derived based on the values representable by
4970+
/// [`StateID`] and [`PatternID`].)
4971+
///
4972+
/// This predicate is useful in contexts where you want to distinguish
4973+
/// between errors related to something provided by an end user (for
4974+
/// example, an invalid regex pattern) and errors related to configured
4975+
/// heuristics. For example, building a DFA might be an optimization that
4976+
/// you want to skip if construction fails because of an exceeded size
4977+
/// limit, but where you want to bubble up an error if it fails for some
4978+
/// other reason.
4979+
///
4980+
/// # Example
4981+
///
4982+
/// ```
4983+
/// # if cfg!(miri) { return Ok(()); } // miri takes too long
4984+
/// # if !cfg!(target_pointer_width = "64") { return Ok(()); } // see #1039
4985+
/// use regex_automata::{dfa::{dense, Automaton}, Input};
4986+
///
4987+
/// let err = dense::Builder::new()
4988+
/// .configure(dense::Config::new()
4989+
/// .determinize_size_limit(Some(100_000))
4990+
/// )
4991+
/// .build(r"\w{20}")
4992+
/// .unwrap_err();
4993+
/// // This error occurs because a size limit was exceeded.
4994+
/// // But things are otherwise valid.
4995+
/// assert!(err.is_size_limit_exceeded());
4996+
///
4997+
/// let err = dense::Builder::new()
4998+
/// .build(r"\bxyz\b")
4999+
/// .unwrap_err();
5000+
/// // This error occurs because a Unicode word boundary
5001+
/// // was used without enabling heuristic support for it.
5002+
/// // So... not related to size limits.
5003+
/// assert!(!err.is_size_limit_exceeded());
5004+
///
5005+
/// let err = dense::Builder::new()
5006+
/// .build(r"(xyz")
5007+
/// .unwrap_err();
5008+
/// // This error occurs because the pattern is invalid.
5009+
/// // So... not related to size limits.
5010+
/// assert!(!err.is_size_limit_exceeded());
5011+
///
5012+
/// # Ok::<(), Box<dyn std::error::Error>>(())
5013+
/// ```
5014+
#[inline]
5015+
pub fn is_size_limit_exceeded(&self) -> bool {
5016+
use self::BuildErrorKind::*;
5017+
5018+
match self.kind {
5019+
NFA(_) | Unsupported(_) => false,
5020+
TooManyStates
5021+
| TooManyStartStates
5022+
| TooManyMatchPatternIDs
5023+
| DFAExceededSizeLimit { .. }
5024+
| DeterminizeExceededSizeLimit { .. } => true,
5025+
}
5026+
}
5027+
}
5028+
49615029
/// The kind of error that occurred during the construction of a DFA.
49625030
///
49635031
/// Note that this error is non-exhaustive. Adding new variants is not

0 commit comments

Comments
 (0)