1use std::{fmt, sync::Arc};
2
3use serde::{Deserialize, Serialize};
4
5use crate::Span;
6
7#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(tag = "kind", rename_all = "snake_case")]
9pub enum ParseErrorKind {
10 UnbalancedParen,
11 UnclosedClass,
12 InvalidEscape(char),
13 DanglingQuantifier,
14 InvalidRange { lo: char, hi: char },
15 InvalidCounted,
16 DuplicateGroupName(Box<str>),
17 UnsupportedFeature(&'static str),
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct ParseError {
22 pub kind: ParseErrorKind,
23 pub span: Span,
24 pub src: Arc<str>,
25}
26
27impl ParseError {
28 pub(crate) fn new(kind: ParseErrorKind, span: Span, src: &Arc<str>) -> Self {
29 Self {
30 kind,
31 span,
32 src: Arc::clone(src),
33 }
34 }
35}
36
37impl fmt::Display for ParseError {
38 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
39 let message = match &self.kind {
40 ParseErrorKind::UnbalancedParen => "unbalanced closing parenthesis".to_owned(),
41 ParseErrorKind::UnclosedClass => "unclosed character class".to_owned(),
42 ParseErrorKind::InvalidEscape(c) => format!("invalid escape: \\{c}"),
43 ParseErrorKind::DanglingQuantifier => "quantifier has no preceding atom".to_owned(),
44 ParseErrorKind::InvalidRange { lo, hi } => format!("invalid class range: {lo}-{hi}"),
45 ParseErrorKind::InvalidCounted => "invalid counted repetition".to_owned(),
46 ParseErrorKind::DuplicateGroupName(name) => format!("duplicate group name: {name}"),
47 ParseErrorKind::UnsupportedFeature(feature) => {
48 format!("unsupported regular-expression feature: {feature}")
49 }
50 };
51 writeln!(formatter, "{message}")?;
52 writeln!(formatter, "{}", self.src)?;
53 let width = (self.span.end - self.span.start).max(1) as usize;
54 write!(
55 formatter,
56 "{}{}",
57 " ".repeat(self.span.start as usize),
58 "^".repeat(width)
59 )
60 }
61}
62
63impl std::error::Error for ParseError {}