use std::{
fmt::{
self,
Display,
Formatter,
},
ops::Range,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Reason {
Unexpected,
Unclosed,
InvalidHexdigit(char),
}
impl Display for Reason {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Unexpected => write!(f, "unexpected input"),
Self::Unclosed => write!(f, "unclosed delimiter"),
Self::InvalidHexdigit(c) => write!(f, "'{c}' is not a hexdigit"),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct SimpleError {
pub(crate) span: Range<usize>,
pub(crate) reason: Reason,
}
impl SimpleError {
pub(crate) fn invalid_hexdigit(span: Range<usize>, found: char) -> Self {
Self {
span,
reason: Reason::InvalidHexdigit(found),
}
}
}
impl chumsky::Error<char> for SimpleError {
type Span = Range<usize>;
type Label = &'static str;
fn expected_input_found<Iter: IntoIterator<Item = Option<char>>>(
span: Self::Span,
_expected: Iter,
_found: Option<char>,
) -> Self {
Self {
span,
reason: Reason::Unexpected,
}
}
fn with_label(self, _label: Self::Label) -> Self {
self
}
fn merge(self, _other: Self) -> Self {
self
}
fn unclosed_delimiter(
_unclosed_span: Self::Span,
_unclosed: char,
span: Self::Span,
_expected: char,
_found: Option<char>,
) -> Self {
Self {
span,
reason: Reason::Unclosed,
}
}
}
#[derive(Clone, Debug)]
pub struct Error<'a> {
pub(crate) source: &'a str,
pub(crate) inner: SimpleError,
}
impl<'a> Error<'a> {
#[must_use]
pub fn span(&self) -> Range<usize> {
self.inner.span.clone()
}
#[must_use]
pub fn reason(&self) -> &Reason {
&self.inner.reason
}
}
impl Display for Error<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let start = self.inner.span.start;
let end = self.inner.span.end;
let span = &self.source[start..end];
write!(
f,
"error while parsing token \"{span}\" in range [{start}, {end})",
)
}
}
impl std::error::Error for Error<'_> {}