use crate::{Parser, Schema, Span};
use annotate_snippets::renderer::DecorStyle;
use annotate_snippets::{AnnotationKind, Group, Level, Snippet};
use std::borrow::Cow;
use std::slice;
pub trait Diagnostic {
fn kind(&self) -> DiagnosticKind;
fn schema_name(&self) -> &str;
fn render(&self, renderer: &Renderer, parser: &Parser) -> String;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DiagnosticKind {
Error,
Warning,
}
#[derive(Debug, Clone)]
pub struct Renderer {
inner: annotate_snippets::Renderer,
}
impl Renderer {
pub fn new(color: bool, unicode: bool, term_width: usize) -> Self {
let mut inner = if color {
annotate_snippets::Renderer::styled()
} else {
annotate_snippets::Renderer::plain()
};
if unicode {
inner = inner.decor_style(DecorStyle::Unicode);
};
inner = inner.term_width(term_width);
Self { inner }
}
pub fn render(&self, diagnostic: &(impl Diagnostic + ?Sized), parser: &Parser) -> String {
diagnostic.render(self, parser)
}
pub(crate) fn error<'a>(&'a self, title: impl Into<Cow<'a, str>>) -> Report<'a> {
Report::new(
Group::with_title(Level::ERROR.primary_title(title)),
&self.inner,
)
}
pub(crate) fn warning<'a>(&'a self, title: impl Into<Cow<'a, str>>) -> Report<'a> {
Report::new(
Group::with_title(Level::WARNING.primary_title(title)),
&self.inner,
)
}
}
pub(crate) struct Report<'a> {
group: Group<'a>,
renderer: &'a annotate_snippets::Renderer,
}
impl<'a> Report<'a> {
fn new(group: Group<'a>, renderer: &'a annotate_snippets::Renderer) -> Self {
Self { group, renderer }
}
pub(crate) fn render(&self) -> String {
self.renderer.render(slice::from_ref(&self.group))
}
pub(crate) fn snippet(
mut self,
schema: &'a Schema,
span: Span,
label: impl Into<Cow<'a, str>>,
) -> Self {
self.group = self.group.element(
Snippet::source(schema.source().unwrap())
.path(schema.path())
.annotation(
AnnotationKind::Primary
.span(span.start..span.end)
.label(Some(label)),
),
);
self
}
pub(crate) fn snippet_with_context(
mut self,
schema: &'a Schema,
main_span: Span,
main_label: impl Into<Cow<'a, str>>,
context_span: Span,
context_label: impl Into<Cow<'a, str>>,
) -> Self {
self.group = self.group.element(
Snippet::source(schema.source().unwrap())
.path(schema.path())
.annotation(
AnnotationKind::Primary
.span(main_span.start..main_span.end)
.label(Some(main_label)),
)
.annotation(
AnnotationKind::Context
.span(context_span.start..context_span.end)
.label(Some(context_label)),
),
);
self
}
pub(crate) fn context(
mut self,
schema: &'a Schema,
span: Span,
label: impl Into<Cow<'a, str>>,
) -> Self {
self.group = self.group.element(
Snippet::source(schema.source().unwrap())
.path(schema.path())
.annotation(
AnnotationKind::Context
.span(span.start..span.end)
.label(Some(label)),
),
);
self
}
pub(crate) fn help(mut self, text: impl Into<Cow<'a, str>>) -> Self {
self.group = self.group.element(Level::HELP.message(text));
self
}
pub(crate) fn note(mut self, text: impl Into<Cow<'a, str>>) -> Self {
self.group = self.group.element(Level::NOTE.message(text));
self
}
}