use crate::error::unexpected_token_message;
use crate::error::Error;
use crate::error::ErrorKind;
use crate::error::SingleError;
use crate::SourceFile;
use crate::Span;
use std::io;
use std::io::Write;
use std::sync::Arc;
use ariadne::Color;
use ariadne::Label;
use ariadne::ReportKind;
use ariadne::Source;
impl ariadne::Span for Span {
type SourceId = String;
fn source(&self) -> &Self::SourceId {
self.source.id()
}
fn start(&self) -> usize {
self.start
}
fn end(&self) -> usize {
self.end
}
}
impl From<&ErrorKind> for ReportKind<'static> {
fn from(value: &ErrorKind) -> Self {
match value {
ErrorKind::Silent
| ErrorKind::Custom { .. }
| ErrorKind::UnknownCharacter(_)
| ErrorKind::UnterminatedGroup { .. }
| ErrorKind::UnterminatedChar(_)
| ErrorKind::LongChar(_)
| ErrorKind::UnterminatedString(_)
| ErrorKind::UnexpectedToken { .. }
| ErrorKind::EndOfFile(_) => ReportKind::Error,
}
}
}
pub struct Report {
report: ariadne::Report<'static, Span>,
source: Arc<SourceFile>,
}
impl Report {
pub fn write<W: Write>(&self, w: W) -> io::Result<()> {
self.report.write(
(
self.source.id().to_owned(),
Source::from(&self.source.contents),
),
w,
)
}
pub fn write_for_stdout<W: Write>(&self, w: W) -> io::Result<()> {
self.report.write_for_stdout(
(
self.source.id().to_owned(),
Source::from(&self.source.contents),
),
w,
)
}
pub fn eprint(&self) -> io::Result<()> {
self.report.eprint((
self.source.id().to_owned(),
Source::from(&self.source.contents),
))
}
pub fn print(&self) -> io::Result<()> {
self.report.print((
self.source.id().to_owned(),
Source::from(&self.source.contents),
))
}
}
impl From<&SingleError> for Report {
fn from(value: &SingleError) -> Self {
let mut builder =
ariadne::Report::build((&value.kind).into(), value.source.id(), value.kind.start())
.with_code(value.kind.code());
match &value.kind {
ErrorKind::Silent => unreachable!(),
ErrorKind::Custom { message, span, .. } => {
builder.set_message(message);
builder.add_label(Label::new(span.clone()).with_color(Color::Red));
}
ErrorKind::UnknownCharacter(span) => {
builder.set_message("Unrecognised character");
builder.add_label(Label::new(span.clone()).with_color(Color::Red));
}
ErrorKind::UnterminatedGroup { start, span } => {
builder.set_message(format!("Unmatched '{start}'"));
builder.add_label(Label::new(span.clone()).with_color(Color::Red));
}
ErrorKind::UnterminatedChar(span) => {
builder.set_message("Expect \"'\" after character literal");
builder.add_label(Label::new(span.clone()).with_color(Color::Red));
}
ErrorKind::LongChar(span) => {
builder.set_message("Character literals must be exactly one character long");
builder.add_label(Label::new(span.clone()).with_color(Color::Red));
}
ErrorKind::UnterminatedString(span) => {
builder.set_message("Expect '\"' at end of string literal");
builder.add_label(Label::new(span.clone()).with_color(Color::Red));
}
ErrorKind::UnexpectedToken { expected, span } => {
builder.set_message("Unexpected token");
builder.add_label(
Label::new(span.clone())
.with_color(Color::Red)
.with_message(unexpected_token_message(expected)),
);
}
ErrorKind::EndOfFile(_) => builder.set_message("Unexpected end of file while parsing"),
}
Report {
report: builder.finish(),
source: Arc::clone(&value.source),
}
}
}
impl From<&Error> for Vec<Report> {
fn from(value: &Error) -> Self {
let mut reports = Vec::with_capacity(value.errors.len());
for error in &value.errors {
if !matches!(&error.kind, ErrorKind::Silent) {
reports.push(Report::from(error));
}
}
reports
}
}
impl Error {
pub fn eprint(&self) -> io::Result<()> {
let reports: Vec<Report> = self.into();
for report in reports {
report.eprint()?;
}
Ok(())
}
pub fn to_reports(&self) -> Vec<Report> {
self.into()
}
}