regast-syntax 0.1.0

Lossless syntax tree for the regast regular expression engine
Documentation
use std::{fmt, sync::Arc};

use serde::{Deserialize, Serialize};

use crate::Span;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ParseErrorKind {
    UnbalancedParen,
    UnclosedClass,
    InvalidEscape(char),
    DanglingQuantifier,
    InvalidRange { lo: char, hi: char },
    InvalidCounted,
    DuplicateGroupName(Box<str>),
    UnsupportedFeature(&'static str),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParseError {
    pub kind: ParseErrorKind,
    pub span: Span,
    pub src: Arc<str>,
}

impl ParseError {
    pub(crate) fn new(kind: ParseErrorKind, span: Span, src: &Arc<str>) -> Self {
        Self {
            kind,
            span,
            src: Arc::clone(src),
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        let message = match &self.kind {
            ParseErrorKind::UnbalancedParen => "unbalanced closing parenthesis".to_owned(),
            ParseErrorKind::UnclosedClass => "unclosed character class".to_owned(),
            ParseErrorKind::InvalidEscape(c) => format!("invalid escape: \\{c}"),
            ParseErrorKind::DanglingQuantifier => "quantifier has no preceding atom".to_owned(),
            ParseErrorKind::InvalidRange { lo, hi } => format!("invalid class range: {lo}-{hi}"),
            ParseErrorKind::InvalidCounted => "invalid counted repetition".to_owned(),
            ParseErrorKind::DuplicateGroupName(name) => format!("duplicate group name: {name}"),
            ParseErrorKind::UnsupportedFeature(feature) => {
                format!("unsupported regular-expression feature: {feature}")
            }
        };
        writeln!(formatter, "{message}")?;
        writeln!(formatter, "{}", self.src)?;
        let width = (self.span.end - self.span.start).max(1) as usize;
        write!(
            formatter,
            "{}{}",
            " ".repeat(self.span.start as usize),
            "^".repeat(width)
        )
    }
}

impl std::error::Error for ParseError {}