use core::fmt::{self, Debug, Display};
use std::{borrow::Cow, error::Error as StdError};
use crate as miette;
use crate::{Diagnostic, Report, SourceCode};
#[repr(transparent)]
pub(crate) struct MessageError<M>(pub(crate) M);
impl<M> Debug for MessageError<M>
where
M: Display + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl<M> Display for MessageError<M>
where
M: Display + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {}
impl<M> Diagnostic for MessageError<M> where M: Display + Debug + 'static {}
#[repr(transparent)]
pub(crate) struct BoxedError(pub(crate) Box<dyn Diagnostic + Send + Sync>);
impl Diagnostic for BoxedError {
fn code(&self) -> Option<Cow<'_, str>> {
self.0.code()
}
fn severity(&self) -> Option<miette::Severity> {
self.0.severity()
}
fn help(&self) -> Option<Cow<'_, str>> {
self.0.help()
}
fn note(&self) -> Option<Cow<'_, str>> {
self.0.note()
}
fn url(&self) -> Option<Cow<'_, str>> {
self.0.url()
}
fn labels(&self) -> crate::Labels {
self.0.labels()
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
self.0.source_code()
}
fn related(&self) -> crate::Related<'_> {
self.0.related()
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
self.0.diagnostic_source()
}
}
impl Debug for BoxedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl Display for BoxedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl StdError for BoxedError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.0.source()
}
fn description(&self) -> &str {
#[allow(deprecated)]
self.0.description()
}
fn cause(&self) -> Option<&dyn StdError> {
#[allow(deprecated)]
self.0.cause()
}
}
pub(crate) struct WithSourceCode<E, C> {
pub(crate) error: E,
pub(crate) source_code: C,
}
impl<E: Diagnostic, C: SourceCode> Diagnostic for WithSourceCode<E, C> {
fn code(&self) -> Option<Cow<'_, str>> {
self.error.code()
}
fn severity(&self) -> Option<miette::Severity> {
self.error.severity()
}
fn help(&self) -> Option<Cow<'_, str>> {
self.error.help()
}
fn note(&self) -> Option<Cow<'_, str>> {
self.error.note()
}
fn url(&self) -> Option<Cow<'_, str>> {
self.error.url()
}
fn labels(&self) -> crate::Labels {
self.error.labels()
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
self.error.source_code().or(Some(&self.source_code))
}
fn related(&self) -> crate::Related<'_> {
self.error.related()
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
self.error.diagnostic_source()
}
}
impl<C: SourceCode> Diagnostic for WithSourceCode<Report, C> {
fn code(&self) -> Option<Cow<'_, str>> {
self.error.code()
}
fn severity(&self) -> Option<miette::Severity> {
self.error.severity()
}
fn help(&self) -> Option<Cow<'_, str>> {
self.error.help()
}
fn note(&self) -> Option<Cow<'_, str>> {
self.error.note()
}
fn url(&self) -> Option<Cow<'_, str>> {
self.error.url()
}
fn labels(&self) -> crate::Labels {
self.error.labels()
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
self.error.source_code().or(Some(&self.source_code))
}
fn related(&self) -> crate::Related<'_> {
self.error.related()
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
self.error.diagnostic_source()
}
}
impl<E: Debug, C> Debug for WithSourceCode<E, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.error, f)
}
}
impl<E: Display, C> Display for WithSourceCode<E, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.error, f)
}
}
impl<E: StdError, C> StdError for WithSourceCode<E, C> {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.error.source()
}
}
impl<C> StdError for WithSourceCode<Report, C> {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.error.source()
}
}
#[cfg(test)]
mod tests {
use thiserror::Error;
use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan, SpanContents};
#[derive(Error, Debug)]
#[error("inner")]
struct Inner {
pub(crate) at: SourceSpan,
pub(crate) source_code: Option<String>,
}
impl Diagnostic for Inner {
fn labels(&self) -> crate::Labels {
crate::Labels::One([LabeledSpan::underline(self.at)])
}
fn source_code(&self) -> Option<&dyn SourceCode> {
self.source_code.as_ref().map(|s| s as _)
}
}
#[derive(Error, Debug)]
#[error("outer")]
#[allow(unused)]
struct Outer {
pub(crate) errors: Vec<Inner>,
}
impl Diagnostic for Outer {
fn related(&self) -> crate::Related<'_> {
self.errors.iter().map(|e| e as &dyn Diagnostic).collect()
}
}
#[test]
fn no_override() {
let inner_source = "hello world";
let outer_source = "abc";
let report =
Report::from(Inner { at: (0..5).into(), source_code: Some(inner_source.to_string()) })
.with_source_code(outer_source.to_string());
let underlined = String::from_utf8(
report.source_code().unwrap().read_span(&(0..5).into(), 0, 0).unwrap().data().to_vec(),
)
.unwrap();
assert_eq!(underlined, "hello");
}
#[test]
#[cfg(feature = "fancy")]
fn two_source_codes() {
let inner_source = "hello world";
let outer_source = "abc";
let report = Report::from(Outer {
errors: vec![
Inner { at: (0..5).into(), source_code: Some(inner_source.to_string()) },
Inner { at: (1..2).into(), source_code: None },
],
})
.with_source_code(outer_source.to_string());
let message = format!("{report:?}");
assert!(message.contains(inner_source));
assert!(message.contains(outer_source));
}
}