use std::{error, fmt};
use crate::{doc_comment::DocComment, span::Span};
use codespan_reporting::diagnostic::{Diagnostic as CodeSpanDiagnostic, Label};
use serde::Serialize;
#[derive(Debug, Default, Serialize)]
pub struct Diagnostic {
pub text: String,
pub start: usize,
pub len: usize,
pub file_id: usize,
pub additional_diagnostics: Vec<Diagnostic>,
}
impl Diagnostic {
pub fn from_span<S: Into<String>>(text: S, span: Span) -> Self {
Self {
text: text.into(),
start: span.start + span.source_offset,
len: span.len,
file_id: span.file_id,
..Default::default()
}
}
pub fn from_doc_comment<S: Into<String>>(text: S, doc_comment: &DocComment) -> Self {
Self {
text: text.into(),
start: doc_comment.start,
len: doc_comment.comment.len(),
file_id: doc_comment.file_id,
..Default::default()
}
}
pub fn attach_diagnostic(&mut self, diagnostic: Diagnostic) -> &Self {
self.additional_diagnostics.push(diagnostic);
self
}
}
impl error::Error for Diagnostic {}
impl fmt::Display for Diagnostic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.text)
}
}
impl From<Diagnostic> for CodeSpanDiagnostic<usize> {
fn from(diagnostic: Diagnostic) -> Self {
let mut labels = vec![Label::primary(
diagnostic.file_id,
diagnostic.start..(diagnostic.start + diagnostic.len),
)
.with_message(&diagnostic.text)];
for additional_diagnostic in diagnostic.additional_diagnostics {
labels.push(
Label::secondary(
additional_diagnostic.file_id,
additional_diagnostic.start
..(additional_diagnostic.start + additional_diagnostic.len),
)
.with_message(&additional_diagnostic.text),
)
}
CodeSpanDiagnostic::error()
.with_message(&diagnostic.text)
.with_labels(labels)
}
}
#[derive(Debug)]
pub struct Diagnostics {
errors: Vec<Diagnostic>,
}
impl Diagnostics {
pub fn into_iter(self) -> impl Iterator<Item = Diagnostic> {
self.errors.into_iter()
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
}
impl From<Vec<Diagnostic>> for Diagnostics {
fn from(errors: Vec<Diagnostic>) -> Self {
Self { errors }
}
}
impl error::Error for Diagnostics {}
impl fmt::Display for Diagnostics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = self
.errors
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>()
.join("\n");
write!(f, "{}", text)
}
}