use std::error::Error as StdError;
use std::fmt;
use crate::call_stack::CallStack;
use crate::codemap::CodeMap;
use crate::codemap::FileSpan;
use crate::codemap::Span;
use crate::span_display::span_display;
pub struct WithDiagnostic<T>(Box<WithDiagnosticInner<T>>);
struct WithDiagnosticInner<T> {
t: T,
diagnostic: Diagnostic,
}
impl<T> WithDiagnostic<T> {
pub fn new_spanned(t: T, span: Span, codemap: &CodeMap) -> Self {
Self(Box::new(WithDiagnosticInner {
t,
diagnostic: Diagnostic {
span: Some(codemap.file_span(span)),
call_stack: CallStack::default(),
},
}))
}
pub(crate) fn new_empty(t: T) -> Self {
Self(Box::new(WithDiagnosticInner {
t,
diagnostic: Diagnostic::default(),
}))
}
pub fn inner(&self) -> &T {
&self.0.t
}
pub fn into_inner(self) -> T {
self.0.t
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> WithDiagnostic<U> {
WithDiagnostic(Box::new(WithDiagnosticInner {
t: f(self.0.t),
diagnostic: self.0.diagnostic,
}))
}
pub fn span(&self) -> Option<&FileSpan> {
self.0.diagnostic.span.as_ref()
}
pub fn call_stack(&self) -> &CallStack {
&self.0.diagnostic.call_stack
}
pub fn set_span(&mut self, span: Span, codemap: &CodeMap) {
if self.0.diagnostic.span.is_none() {
self.0.diagnostic.span = Some(codemap.file_span(span));
}
}
pub fn set_call_stack(&mut self, call_stack: impl FnOnce() -> CallStack) {
if self.0.diagnostic.call_stack.is_empty() {
self.0.diagnostic.call_stack = call_stack();
}
}
}
impl<T: StdError> fmt::Display for WithDiagnostic<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let with_context = f.alternate() && self.0.t.source().is_some();
diagnostic_display(self, false, f, with_context)
}
}
impl<T: StdError> fmt::Debug for WithDiagnostic<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diagnostic_display(self, false, f, true)
}
}
impl<T: Into<crate::Error>> From<WithDiagnostic<T>> for crate::Error {
fn from(e: WithDiagnostic<T>) -> Self {
let diagnostic = e.0.diagnostic;
let mut e: crate::Error = e.0.t.into();
e.0.0.diagnostic = diagnostic;
e
}
}
#[derive(Debug, Default)]
struct Diagnostic {
span: Option<FileSpan>,
call_stack: CallStack,
}
impl Diagnostic {
fn get_display_list<'a>(
&'a self,
annotation_label: &'a str,
color: bool,
) -> impl fmt::Display + 'a {
span_display(
self.span.as_ref().map(|s| s.as_ref()),
annotation_label,
color,
)
}
}
pub(crate) fn diagnostic_display<T: fmt::Debug + fmt::Display>(
d: &WithDiagnostic<T>,
color: bool,
f: &mut dyn fmt::Write,
with_context: bool,
) -> fmt::Result {
write!(f, "{}", d.call_stack())?;
let annotation_label = format!("{}", d.inner());
let display_list = d.0.diagnostic.get_display_list(&annotation_label, color);
writeln!(f, "{}", display_list)?;
if with_context {
writeln!(f, "\n\n{:?}", d.inner())?;
}
Ok(())
}