Skip to main content

fuzzy_regex/
error.rs

1//! Error types for the fuzzy regex engine.
2
3use std::fmt;
4use thiserror::Error;
5
6/// The main error type for fuzzy regex operations.
7#[derive(Error, Debug, Clone, PartialEq, Eq)]
8pub enum Error {
9    /// Error during pattern parsing.
10    #[error("parse error at position {position}: {message}")]
11    Parse {
12        /// Byte position in the pattern where the error occurred.
13        position: usize,
14        /// Description of the parse error.
15        message: String,
16    },
17
18    /// Invalid escape sequence.
19    #[error("invalid escape sequence '\\{char}' at position {position}")]
20    InvalidEscape {
21        /// The invalid escape character.
22        char: char,
23        /// Byte position in the pattern where the invalid escape occurred.
24        position: usize,
25    },
26
27    /// Unclosed group or bracket.
28    #[error("unclosed {kind} starting at position {position}")]
29    Unclosed {
30        /// The kind of unclosed delimiter (e.g., "group", "bracket").
31        kind: &'static str,
32        /// Byte position in the pattern where the unclosed delimiter starts.
33        position: usize,
34    },
35
36    /// Invalid quantifier.
37    #[error("invalid quantifier at position {position}: {message}")]
38    InvalidQuantifier {
39        /// Byte position in the pattern where the invalid quantifier occurred.
40        position: usize,
41        /// Description of why the quantifier is invalid.
42        message: String,
43    },
44
45    /// Invalid character class.
46    #[error("invalid character class at position {position}: {message}")]
47    InvalidCharClass {
48        /// Byte position in the pattern where the invalid character class occurred.
49        position: usize,
50        /// Description of why the character class is invalid.
51        message: String,
52    },
53
54    /// Invalid fuzziness specification.
55    #[error("invalid fuzziness specification at position {position}: {message}")]
56    InvalidFuzziness {
57        /// Byte position in the pattern where the invalid fuzziness specification occurred.
58        position: usize,
59        /// Description of why the fuzziness specification is invalid.
60        message: String,
61    },
62
63    /// Invalid backreference.
64    #[error("invalid backreference \\{group} at position {position}: {message}")]
65    InvalidBackreference {
66        /// The backreference group number that is invalid.
67        group: usize,
68        /// Byte position in the pattern where the invalid backreference occurred.
69        position: usize,
70        /// Description of why the backreference is invalid.
71        message: String,
72    },
73
74    /// Empty pattern where one was required.
75    #[error("empty pattern not allowed")]
76    EmptyPattern,
77
78    /// Pattern too complex (e.g., too many states).
79    #[error("pattern too complex: {message}")]
80    TooComplex {
81        /// Description of why the pattern is too complex.
82        message: String,
83    },
84
85    /// Match operation timed out.
86    #[error("match timed out after {duration:?}")]
87    Timeout {
88        /// The timeout duration that was exceeded.
89        duration: std::time::Duration,
90    },
91}
92
93impl Error {
94    /// Create a parse error at a given position.
95    #[must_use]
96    pub fn parse(position: usize, message: impl Into<String>) -> Self {
97        Error::Parse {
98            position,
99            message: message.into(),
100        }
101    }
102
103    /// Create an invalid escape error.
104    #[must_use]
105    pub fn invalid_escape(char: char, position: usize) -> Self {
106        Error::InvalidEscape { char, position }
107    }
108
109    /// Create an unclosed delimiter error.
110    #[must_use]
111    pub fn unclosed(kind: &'static str, position: usize) -> Self {
112        Error::Unclosed { kind, position }
113    }
114
115    /// Create an invalid quantifier error.
116    #[must_use]
117    pub fn invalid_quantifier(position: usize, message: impl Into<String>) -> Self {
118        Error::InvalidQuantifier {
119            position,
120            message: message.into(),
121        }
122    }
123
124    /// Create an invalid character class error.
125    #[must_use]
126    pub fn invalid_char_class(position: usize, message: impl Into<String>) -> Self {
127        Error::InvalidCharClass {
128            position,
129            message: message.into(),
130        }
131    }
132
133    /// Create an invalid fuzziness error.
134    #[must_use]
135    pub fn invalid_fuzziness(position: usize, message: impl Into<String>) -> Self {
136        Error::InvalidFuzziness {
137            position,
138            message: message.into(),
139        }
140    }
141
142    /// Create an invalid backreference error.
143    #[must_use]
144    pub fn invalid_backreference(
145        group: usize,
146        position: usize,
147        message: impl Into<String>,
148    ) -> Self {
149        Error::InvalidBackreference {
150            group,
151            position,
152            message: message.into(),
153        }
154    }
155}
156
157/// A specialized Result type for fuzzy regex operations.
158pub type Result<T> = std::result::Result<T, Error>;
159
160/// Span in the input pattern for error reporting.
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162pub struct Span {
163    /// Start position (byte offset, inclusive).
164    pub start: usize,
165    /// End position (byte offset, exclusive).
166    pub end: usize,
167}
168
169impl Span {
170    /// Create a span from start to end positions.
171    #[must_use]
172    pub fn new(start: usize, end: usize) -> Self {
173        Span { start, end }
174    }
175
176    /// Create a single-character span at the given position.
177    #[must_use]
178    pub fn at(position: usize) -> Self {
179        Span {
180            start: position,
181            end: position + 1,
182        }
183    }
184}
185
186impl fmt::Display for Span {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        if self.start == self.end || self.start + 1 == self.end {
189            write!(f, "position {}", self.start)
190        } else {
191            write!(f, "positions {}-{}", self.start, self.end)
192        }
193    }
194}