use crate::{ErrorType, Indexer, Kind, Span};
use alloc::{
boxed::Box,
string::{String, ToString as _},
};
use core::{error::Error, fmt};
use miette::{
Diagnostic, LabeledSpan, MietteError, MietteSpanContents, ReportHandler, Severity, SourceCode,
SourceSpan, SpanContents,
};
pub(crate) struct Wrapper<'a, T: ?Sized, S>(&'a T, SpanWrapper<S>);
impl<'a, T: ErrorType<Span = S> + ?Sized, S: Span + Default> Wrapper<'a, T, S> {
pub(crate) fn new(value: &'a T) -> Self {
Self(value, SpanWrapper(value.primary_span().unwrap_or_default()))
}
}
impl<T: ErrorType + 'static, S: Span + Send + Sync> Wrapper<'_, T, S> {
pub(crate) fn fmt_with(&self, handler: &impl ReportHandler) -> String {
WrapperWithHandler(self, handler).to_string()
}
}
impl<T: ErrorType + ?Sized, S> fmt::Debug for Wrapper<'_, T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.primary_message())
}
}
impl<T: ErrorType + ?Sized, S> fmt::Display for Wrapper<'_, T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.primary_message())
}
}
impl<T: ErrorType + ?Sized, S> Error for Wrapper<'_, T, S> {}
impl<T: ErrorType + ?Sized, S: Span + Send + Sync> Diagnostic for Wrapper<'_, T, S> {
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
Some(Box::new(self.0.code()))
}
fn severity(&self) -> Option<Severity> {
match self.0.kind() {
Kind::Error => Some(Severity::Error),
Kind::Warn => Some(Severity::Warning),
}
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
Some(&self.1)
}
fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
Some(Box::new(self.0.primary_span()?.uri().clone()))
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
let primary_span = self.0.primary_span()?;
let iter = [LabeledSpan::new_primary_with_span(
Some(self.0.primary_label().to_string()),
SourceSpan::new(
primary_span.start().into(),
primary_span.end() - primary_span.start(),
),
)]
.into_iter();
Some(Box::new(iter))
}
}
struct WrapperWithHandler<'a, T, S, H: ?Sized>(&'a Wrapper<'a, T, S>, &'a H);
impl<T: ErrorType + 'static, S: Span + Send + Sync, H: ReportHandler + ?Sized> fmt::Display
for WrapperWithHandler<'_, T, S, H>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.1.debug(self.0, f)
}
}
struct SpanWrapper<S>(S);
impl<S: Span + Send + Sync> SourceCode for SpanWrapper<S> {
fn read_span<'a>(
&'a self,
span: &SourceSpan,
context_lines_before: usize,
context_lines_after: usize,
) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
let index = self.0.source_index();
let (start, end) = index.span_with_context_lines(
span.offset(),
span.offset() + span.len(),
context_lines_before,
context_lines_after,
);
let (start_line, start_column) = index.line_col_at(span.offset());
let (end_line, _) = index.line_col_at(span.offset() + span.len());
let name = self.0.uri().to_string();
let data = &self.0.source_text().as_ref().as_bytes()[start..end];
Ok(Box::new(MietteSpanContents::new_named(
name,
data,
SourceSpan::new(start.into(), end - start),
start_line,
start_column,
end_line - start_line + 1,
)))
}
}