forbidden_regex/error.rs
1//! What: Compile-time error type for the forbidden-regex engine.
2//! Why: This file is the Rust module that groups the error implementation, so the
3//! compiler gives those items one namespace and sibling modules can import that name.
4//!
5//! In TS you'd write (pseudocode):
6//! ```ts
7//! // module error: see exported functions and types below.
8//! ```
9
10/// What: Imports the formatter pieces used to render a human-readable error message.
11/// Why: The code below uses `fmt` directly; importing from `std` keeps each call site
12/// focused on the matcher logic instead of the full Rust path.
13///
14/// In TS you'd write (pseudocode):
15/// ```ts
16/// import { fmt } from "std";
17/// ```
18use std::fmt;
19
20/// Reasons a pattern (or a serialized DFA) is rejected before it can match.
21///
22/// What: every failure path in parsing, the empty-match guard, the DFA state
23/// cap, and (de)serialization funnels into one of these variants.
24/// Why: callers (the scanner, tests) get a single typed error to match on, and
25/// `compile`/`new` never panic on bad input; they return one of these instead.
26///
27/// In TS you'd write (pseudocode):
28/// ```ts
29/// type CompileError =
30/// | { kind: "variant" };
31/// ```
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum CompileError {
34 /// A syntax or unsupported-construct rejection at a byte offset.
35 ///
36 /// What: holds the offset into the pattern and a message naming what was
37 /// wrong (unsupported operator, bad escape, unbalanced bracket, operand not
38 /// a single atom, mixed operators, stacked quantifier, bad repetition).
39 /// Why: one variant covers every parse rejection so the surface stays small
40 /// while still pointing the rule author at the exact spot.
41 ///
42 /// In TS you'd write (pseudocode):
43 /// ```ts
44 /// // Same step as the Rust statement below, written with ordinary TS objects/functions.
45 /// ```
46 Syntax {
47 /// What: Byte offset into the pattern where the problem was detected.
48 /// Why: `pos` stores byte offset into the pattern where the problem was detected, so
49 /// matcher code reads that precomputed state by name instead of recomputing or
50 /// passing it separately.
51 ///
52 /// In TS you'd write (pseudocode):
53 /// ```ts
54 /// pos: number;
55 /// ```
56 pos: usize,
57 /// What: Human-readable description of what was rejected.
58 /// Why: `message` stores human-readable description of what was rejected, so matcher
59 /// code reads that precomputed state by name instead of recomputing or passing
60 /// it separately.
61 ///
62 /// In TS you'd write (pseudocode):
63 /// ```ts
64 /// message: string;
65 /// ```
66 message: String,
67 },
68 /// The pattern can match the empty string, so under unanchored search it
69 /// would match every input.
70 ///
71 /// What: raised when the parsed root is nullable in some realizable anchor
72 /// context. Why: such a rule is a footgun (flags every line); rejecting it
73 /// at compile time is safer than silently matching everything.
74 ///
75 /// In TS you'd write (pseudocode):
76 /// ```ts
77 /// // Same step as the Rust statement below, written with ordinary TS objects/functions.
78 /// ```
79 EmptyMatchable,
80 /// Determinization produced more states than the configured cap.
81 ///
82 /// What: complement and intersection can blow up the state count on
83 /// pathological patterns. Why: a hard cap turns that into a clean error
84 /// instead of unbounded memory use.
85 ///
86 /// In TS you'd write (pseudocode):
87 /// ```ts
88 /// // Same step as the Rust statement below, written with ordinary TS objects/functions.
89 /// ```
90 StateCap {
91 /// What: State-count limit that was exceeded.
92 /// Why: `limit` stores state-count limit that was exceeded, so matcher code reads
93 /// that precomputed state by name instead of recomputing or passing it
94 /// separately.
95 ///
96 /// In TS you'd write (pseudocode):
97 /// ```ts
98 /// limit: number;
99 /// ```
100 limit: usize,
101 },
102 /// Encoding a compiled automaton to bytes failed.
103 ///
104 /// What: wraps a bincode serialization failure as a string. Why: keeps the
105 /// public error free of a bincode type in its signature.
106 ///
107 /// In TS you'd write (pseudocode):
108 /// ```ts
109 /// // Same step as the Rust statement below, written with ordinary TS objects/functions.
110 /// ```
111 Serialize {
112 /// What: Underlying codec failure rendered as text.
113 /// Why: `message` stores underlying codec failure rendered as text, so matcher code
114 /// reads that precomputed state by name instead of recomputing or passing it
115 /// separately.
116 ///
117 /// In TS you'd write (pseudocode):
118 /// ```ts
119 /// message: string;
120 /// ```
121 message: String,
122 },
123 /// A deserialized automaton failed structural validation.
124 ///
125 /// What: raised by `from_bytes` when decoded indices are out of bounds or
126 /// lengths are inconsistent. Why: a hostile or corrupt blob must be rejected
127 /// before it is ever executed, so the match loop can never read out of
128 /// bounds.
129 ///
130 /// In TS you'd write (pseudocode):
131 /// ```ts
132 /// // Same step as the Rust statement below, written with ordinary TS objects/functions.
133 /// ```
134 Invalid {
135 /// What: Description of which invariant the decoded automaton violated.
136 /// Why: `message` stores description of which invariant the decoded automaton
137 /// violated, so matcher code reads that precomputed state by name instead of
138 /// recomputing or passing it separately.
139 ///
140 /// In TS you'd write (pseudocode):
141 /// ```ts
142 /// message: string;
143 /// ```
144 message: String,
145 },
146}
147
148/// What: Renders a `CompileError` for end users and logs.
149/// Why: The program attaches these functions to the named Rust type so callers can use
150/// method syntax.
151///
152/// In TS you'd write (pseudocode):
153/// ```ts
154/// // Methods are written inside a class or as functions that take the value.
155/// ```
156impl fmt::Display for CompileError {
157 /// Writes a one-line description of the error.
158 ///
159 /// What: matches each variant to a sentence. Why: `Display` is what the
160 /// scanner surfaces and what `Error` builds on.
161 ///
162 /// In TS you'd write (pseudocode):
163 /// ```ts
164 /// function fmt(f: fmt.Formatter<'_>): fmt.Result {
165 /// // Rust body below is the implementation.
166 /// }
167 /// ```
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 // What: branch per variant; no `match` value is discarded.
170 // Why: each variant carries different fields to interpolate.
171 //
172 // In TS you'd write (pseudocode):
173 // ```ts
174 // // Same step as the Rust statement below, written with ordinary TS objects/functions.
175 // ```
176 match self {
177 CompileError::Syntax { pos, message } => {
178 return write!(f, "syntax error at byte {pos}: {message}")
179 }
180 CompileError::EmptyMatchable => {
181 return write!(f, "pattern can match the empty string, which would match every input")
182 }
183 CompileError::StateCap { limit } => {
184 return write!(f, "pattern exceeded the DFA state cap of {limit}")
185 }
186 CompileError::Serialize { message } => {
187 return write!(f, "failed to serialize automaton: {message}")
188 }
189 CompileError::Invalid { message } => {
190 return write!(f, "invalid serialized automaton: {message}")
191 }
192 }
193 }
194}
195
196/// What: Lets `CompileError` participate in the standard error ecosystem.
197/// Why: The program attaches these functions to the named Rust type so callers can use
198/// method syntax.
199///
200/// In TS you'd write (pseudocode):
201/// ```ts
202/// // Methods are written inside a class or as functions that take the value.
203/// ```
204impl std::error::Error for CompileError {}
205
206/// What: Unit tests for error rendering, in a sidecar (max-lines exempt).
207/// Why: The package keeps that concept in a separate Rust file so this module can refer to
208/// it by name.
209///
210/// In TS you'd write (pseudocode):
211/// ```ts
212/// import "./tests";
213/// ```
214#[cfg(test)]
215#[path = "error_tests.rs"]
216mod tests;