1use alloc::{
2    format,
3    string::{String, ToString},
4    vec,
5    vec::Vec,
6};
7
8use crate::{ast, hir};
9
10#[non_exhaustive]
15#[derive(Clone, Debug, Eq, PartialEq)]
16pub enum Error {
17    Parse(ast::Error),
20    Translate(hir::Error),
23}
24
25impl From<ast::Error> for Error {
26    fn from(err: ast::Error) -> Error {
27        Error::Parse(err)
28    }
29}
30
31impl From<hir::Error> for Error {
32    fn from(err: hir::Error) -> Error {
33        Error::Translate(err)
34    }
35}
36
37#[cfg(feature = "std")]
38impl std::error::Error for Error {}
39
40impl core::fmt::Display for Error {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        match *self {
43            Error::Parse(ref x) => x.fmt(f),
44            Error::Translate(ref x) => x.fmt(f),
45        }
46    }
47}
48
49#[derive(Debug)]
55pub struct Formatter<'e, E> {
56    pattern: &'e str,
58    err: &'e E,
60    span: &'e ast::Span,
62    aux_span: Option<&'e ast::Span>,
65}
66
67impl<'e> From<&'e ast::Error> for Formatter<'e, ast::ErrorKind> {
68    fn from(err: &'e ast::Error) -> Self {
69        Formatter {
70            pattern: err.pattern(),
71            err: err.kind(),
72            span: err.span(),
73            aux_span: err.auxiliary_span(),
74        }
75    }
76}
77
78impl<'e> From<&'e hir::Error> for Formatter<'e, hir::ErrorKind> {
79    fn from(err: &'e hir::Error) -> Self {
80        Formatter {
81            pattern: err.pattern(),
82            err: err.kind(),
83            span: err.span(),
84            aux_span: None,
85        }
86    }
87}
88
89impl<'e, E: core::fmt::Display> core::fmt::Display for Formatter<'e, E> {
90    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91        let spans = Spans::from_formatter(self);
92        if self.pattern.contains('\n') {
93            let divider = repeat_char('~', 79);
94
95            writeln!(f, "regex parse error:")?;
96            writeln!(f, "{}", divider)?;
97            let notated = spans.notate();
98            write!(f, "{}", notated)?;
99            writeln!(f, "{}", divider)?;
100            if !spans.multi_line.is_empty() {
103                let mut notes = vec![];
104                for span in &spans.multi_line {
105                    notes.push(format!(
106                        "on line {} (column {}) through line {} (column {})",
107                        span.start.line,
108                        span.start.column,
109                        span.end.line,
110                        span.end.column - 1
111                    ));
112                }
113                writeln!(f, "{}", notes.join("\n"))?;
114            }
115            write!(f, "error: {}", self.err)?;
116        } else {
117            writeln!(f, "regex parse error:")?;
118            let notated = Spans::from_formatter(self).notate();
119            write!(f, "{}", notated)?;
120            write!(f, "error: {}", self.err)?;
121        }
122        Ok(())
123    }
124}
125
126struct Spans<'p> {
135    pattern: &'p str,
137    line_number_width: usize,
143    by_line: Vec<Vec<ast::Span>>,
148    multi_line: Vec<ast::Span>,
152}
153
154impl<'p> Spans<'p> {
155    fn from_formatter<'e, E: core::fmt::Display>(
157        fmter: &'p Formatter<'e, E>,
158    ) -> Spans<'p> {
159        let mut line_count = fmter.pattern.lines().count();
160        if fmter.pattern.ends_with('\n') {
164            line_count += 1;
165        }
166        let line_number_width =
167            if line_count <= 1 { 0 } else { line_count.to_string().len() };
168        let mut spans = Spans {
169            pattern: &fmter.pattern,
170            line_number_width,
171            by_line: vec![vec![]; line_count],
172            multi_line: vec![],
173        };
174        spans.add(fmter.span.clone());
175        if let Some(span) = fmter.aux_span {
176            spans.add(span.clone());
177        }
178        spans
179    }
180
181    fn add(&mut self, span: ast::Span) {
183        if span.is_one_line() {
186            let i = span.start.line - 1; self.by_line[i].push(span);
188            self.by_line[i].sort();
189        } else {
190            self.multi_line.push(span);
191            self.multi_line.sort();
192        }
193    }
194
195    fn notate(&self) -> String {
198        let mut notated = String::new();
199        for (i, line) in self.pattern.lines().enumerate() {
200            if self.line_number_width > 0 {
201                notated.push_str(&self.left_pad_line_number(i + 1));
202                notated.push_str(": ");
203            } else {
204                notated.push_str("    ");
205            }
206            notated.push_str(line);
207            notated.push('\n');
208            if let Some(notes) = self.notate_line(i) {
209                notated.push_str(¬es);
210                notated.push('\n');
211            }
212        }
213        notated
214    }
215
216    fn notate_line(&self, i: usize) -> Option<String> {
221        let spans = &self.by_line[i];
222        if spans.is_empty() {
223            return None;
224        }
225        let mut notes = String::new();
226        for _ in 0..self.line_number_padding() {
227            notes.push(' ');
228        }
229        let mut pos = 0;
230        for span in spans {
231            for _ in pos..(span.start.column - 1) {
232                notes.push(' ');
233                pos += 1;
234            }
235            let note_len = span.end.column.saturating_sub(span.start.column);
236            for _ in 0..core::cmp::max(1, note_len) {
237                notes.push('^');
238                pos += 1;
239            }
240        }
241        Some(notes)
242    }
243
244    fn left_pad_line_number(&self, n: usize) -> String {
247        let n = n.to_string();
248        let pad = self.line_number_width.checked_sub(n.len()).unwrap();
249        let mut result = repeat_char(' ', pad);
250        result.push_str(&n);
251        result
252    }
253
254    fn line_number_padding(&self) -> usize {
260        if self.line_number_width == 0 {
261            4
262        } else {
263            2 + self.line_number_width
264        }
265    }
266}
267
268fn repeat_char(c: char, count: usize) -> String {
269    core::iter::repeat(c).take(count).collect()
270}
271
272#[cfg(test)]
273mod tests {
274    use alloc::string::ToString;
275
276    use crate::ast::parse::Parser;
277
278    fn assert_panic_message(pattern: &str, expected_msg: &str) {
279        let result = Parser::new().parse(pattern);
280        match result {
281            Ok(_) => {
282                panic!("regex should not have parsed");
283            }
284            Err(err) => {
285                assert_eq!(err.to_string(), expected_msg.trim());
286            }
287        }
288    }
289
290    #[test]
292    fn regression_464() {
293        let err = Parser::new().parse("a{\n").unwrap_err();
294        assert!(!err.to_string().is_empty());
296    }
297
298    #[test]
300    fn repetition_quantifier_expects_a_valid_decimal() {
301        assert_panic_message(
302            r"\\u{[^}]*}",
303            r#"
304regex parse error:
305    \\u{[^}]*}
306        ^
307error: repetition quantifier expects a valid decimal
308"#,
309        );
310    }
311}