Skip to content

Commit 13fd108

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 13fd108

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

regex-automata/src/dfa/dense.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4958,6 +4958,73 @@ pub struct BuildError {
49584958
kind: BuildErrorKind,
49594959
}
49604960

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

0 commit comments

Comments
 (0)