use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFiles,
term,
};
use std::{error::Error as StdError, fmt, io::Cursor, ops::Range, sync::Arc};
use termcolor::NoColor;
#[derive(Debug)]
pub(crate) struct ParseError {
file_info: Arc<FileInfo>,
pub(crate) annotations: Vec<Annotation>,
pub(crate) message: String,
}
impl ParseError {
pub(crate) fn new<M: Into<String>>(
file_info: Arc<FileInfo>,
annotations: Vec<Annotation>,
message: M,
) -> ParseError {
ParseError {
file_info,
annotations,
message: message.into(),
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut files = SimpleFiles::new();
let file_id = files.add(&self.file_info.name, &self.file_info.contents);
let diagnostic = Diagnostic::error().with_message(&self.message).with_labels(
self.annotations
.iter()
.map(|a| match a.ty {
AnnotationType::Primary => {
Label::primary(file_id, &a.location).with_message(&a.message)
}
AnnotationType::Secondary => {
Label::secondary(file_id, &a.location).with_message(&a.message)
}
})
.collect(),
);
let mut buf = Vec::with_capacity(1024);
let mut wtr = NoColor::new(Cursor::new(&mut buf));
let config = codespan_reporting::term::Config::default();
term::emit(&mut wtr, &config, &files, &diagnostic).map_err(|_| fmt::Error)?;
write!(f, "{}", String::from_utf8_lossy(&buf))
}
}
impl StdError for ParseError {}
#[derive(Debug)]
pub(crate) struct FileInfo {
pub(crate) name: String,
pub(crate) contents: String,
}
impl FileInfo {
pub(crate) fn new(name: String, contents: String) -> Self {
Self { name, contents }
}
}
#[derive(Debug)]
pub(crate) struct Annotation {
pub(crate) ty: AnnotationType,
pub(crate) location: Location,
pub(crate) message: String,
}
impl Annotation {
pub(crate) fn primary<L, M>(location: L, message: M) -> Self
where
L: Into<Location>,
M: Into<String>,
{
Annotation {
ty: AnnotationType::Primary,
location: location.into(),
message: message.into(),
}
}
pub(crate) fn secondary<L, M>(location: L, message: M) -> Self
where
L: Into<Location>,
M: Into<String>,
{
Annotation {
ty: AnnotationType::Secondary,
location: location.into(),
message: message.into(),
}
}
}
#[derive(Debug)]
pub(crate) enum AnnotationType {
Primary,
Secondary,
}
#[derive(Debug)]
pub(crate) enum Location {
Position(usize),
Range(Range<usize>),
}
impl From<usize> for Location {
fn from(pos: usize) -> Self {
Location::Position(pos)
}
}
impl From<Range<usize>> for Location {
fn from(range: Range<usize>) -> Self {
Location::Range(range)
}
}
impl<'a> From<&'a Location> for Range<usize> {
fn from(input: &'a Location) -> Self {
match input {
Location::Position(p) => *p..(*p + 1),
Location::Range(r) => r.to_owned(),
}
}
}