1use core::{fmt, ops, str};
4
5use compile_fmt::{compile_panic, Ascii};
6
7#[derive(Debug)]
9pub struct Error {
10 pos: ops::Range<usize>,
11 kind: ErrorKind,
12}
13
14impl fmt::Display for Error {
15 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(formatter, "invalid regex at {:?}: {}", self.pos, self.kind)
17 }
18}
19
20#[cfg(feature = "std")]
21impl std::error::Error for Error {}
22
23impl Error {
24 pub fn kind(&self) -> &ErrorKind {
26 &self.kind
27 }
28
29 pub fn pos(&self) -> ops::Range<usize> {
31 self.pos.clone()
32 }
33
34 #[track_caller]
35 pub(crate) const fn compile_panic(self, regex: &str) -> ! {
36 let (_, hl) = regex.as_bytes().split_at(self.pos.start);
37 let (hl, _) = hl.split_at(self.pos.end - self.pos.start);
38 let Ok(hl) = str::from_utf8(hl) else {
39 panic!("internal error: invalid error range");
40 };
41
42 compile_panic!(
43 "invalid regex at ",
44 self.pos.start => compile_fmt::fmt::<usize>(), "..", self.pos.end => compile_fmt::fmt::<usize>(),
45 " ('", hl => compile_fmt::clip(64, "…"),
46 "'): ", self.kind.as_ascii_str() => compile_fmt::clip_ascii(32, "")
47 );
48 }
49}
50
51#[derive(Debug)]
53#[non_exhaustive]
54pub enum ErrorKind {
55 MissingRepetition,
57 UnfinishedRepetition,
59 EmptyDecimal,
61 InvalidDecimal,
63 EmptyHex,
65 InvalidHex,
67 NonUnicodeHex,
69 InvalidRepetitionRange,
71 UnfinishedEscape,
73 UnsupportedBackref,
75 UnsupportedEscape,
77 UnfinishedWordBoundary,
79 UnknownWordBoundary,
81 UnicodeClassesNotSupported,
83 LookaroundNotSupported,
85 UnfinishedCaptureName,
87 EmptyCaptureName,
89 InvalidCaptureName,
91 NonAsciiCaptureName,
93 DuplicateCaptureName {
95 prev_pos: ops::Range<usize>,
97 },
98 UnfinishedGroup,
100 NonMatchingGroupEnd,
102 UnfinishedSet,
104 InvalidRangeStart,
106 InvalidRangeEnd,
108 InvalidRange,
110 InvalidEscapeInSet,
113 UnfinishedFlags,
115 UnfinishedFlagsNegation,
117 RepeatedFlagNegation,
119 UnsupportedFlag,
121 RepeatedFlag {
123 contradicting: bool,
125 },
126
127 DisallowedWhitespace,
130 DisallowedComment,
139
140 AstOverflow,
142 GroupDepthOverflow,
144 NamedGroupOverflow,
146}
147
148impl fmt::Display for ErrorKind {
149 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
150 formatter.write_str(self.as_str())
151 }
152}
153
154impl ErrorKind {
155 pub(crate) const fn with_position(self, pos: ops::Range<usize>) -> Error {
156 Error { pos, kind: self }
157 }
158
159 const fn as_str(&self) -> &'static str {
160 match self {
161 Self::MissingRepetition => "missing repetition",
162 Self::UnfinishedRepetition => "unfinished repetition",
163 Self::EmptyDecimal => "empty decimal number",
164 Self::InvalidDecimal => "invalid decimal number",
165 Self::EmptyHex => "empty hex escape",
166 Self::InvalidHex => "invalid hex escape",
167 Self::NonUnicodeHex => "non-Unicode hex escape",
168 Self::InvalidRepetitionRange => "invalid repetition range",
169 Self::UnfinishedEscape => "unfinished escape",
170 Self::UnsupportedBackref => "backreferences (e.g., \\1) are not supported",
171 Self::UnsupportedEscape => "escape is not supported",
172 Self::UnfinishedWordBoundary => "unfinished word boundary",
173 Self::UnknownWordBoundary => "unknown word boundary",
174 Self::UnicodeClassesNotSupported => "Unicode classes are not supported",
175 Self::LookaroundNotSupported => "lookaround groups are not supported",
176 Self::UnfinishedCaptureName => "unfinished capture name",
177 Self::EmptyCaptureName => "empty capture name",
178 Self::InvalidCaptureName => "invalid capture name",
179 Self::NonAsciiCaptureName => "non-ASCII capture names are not supported",
180 Self::DuplicateCaptureName { .. } => "duplicate capture name",
181 Self::UnfinishedGroup => "unfinished group",
182 Self::NonMatchingGroupEnd => "non-matching group end",
183 Self::GroupDepthOverflow => "too deeply nested group",
184 Self::UnfinishedSet => "unfinished set",
185 Self::InvalidEscapeInSet => "invalid escape in set [..]",
186 Self::InvalidRangeStart => "invalid range start",
187 Self::InvalidRangeEnd => "invalid range end",
188 Self::InvalidRange => "invalid range",
189 Self::UnfinishedFlags => "unfinished flags",
190 Self::UnfinishedFlagsNegation => "unfinished flags negation",
191 Self::RepeatedFlagNegation => "repeated flag negation",
192 Self::UnsupportedFlag => "unsupported flag",
193 Self::RepeatedFlag {
194 contradicting: true,
195 } => "contradicting flag value",
196 Self::RepeatedFlag {
197 contradicting: false,
198 } => "repeated flag value",
199 Self::DisallowedWhitespace => "disallowed whitespace (e.g., inside a hex escape)",
200 Self::DisallowedComment => "disallowed comment (e.g., inside a hex escape)",
201 Self::AstOverflow => "too many AST nodes",
202 Self::NamedGroupOverflow => "too many named groups",
203 }
204 }
205
206 const fn as_ascii_str(&self) -> Ascii<'static> {
207 Ascii::new(self.as_str())
208 }
209}