use core::fmt::{self, Debug, Display}; use std::error::Error as StdError; use crate::{Diagnostic, LabeledSpan, Report, SourceCode}; use crate as miette; #[repr(transparent)] pub(crate) struct DisplayError(pub(crate) M); #[repr(transparent)] pub(crate) struct MessageError(pub(crate) M); pub(crate) struct NoneError; impl Debug for DisplayError where M: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl Display for DisplayError where M: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl StdError for DisplayError where M: Display + 'static {} impl Diagnostic for DisplayError where M: Display + 'static {} impl Debug for MessageError where M: Display + Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) } } impl Display for MessageError where M: Display + Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl StdError for MessageError where M: Display + Debug + 'static {} impl Diagnostic for MessageError where M: Display + Debug + 'static {} impl Debug for NoneError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt("Option was None", f) } } impl Display for NoneError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt("Option was None", f) } } impl StdError for NoneError {} impl Diagnostic for NoneError {} #[repr(transparent)] pub(crate) struct BoxedError(pub(crate) Box); impl Diagnostic for BoxedError { fn code<'a>(&'a self) -> Option> { self.0.code() } fn severity(&self) -> Option { self.0.severity() } fn help<'a>(&'a self) -> Option> { self.0.help() } fn url<'a>(&'a self) -> Option> { self.0.url() } fn labels<'a>(&'a self) -> Option + 'a>> { self.0.labels() } fn source_code(&self) -> Option<&dyn miette::SourceCode> { self.0.source_code() } fn related<'a>(&'a self) -> Option + 'a>> { 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 { pub(crate) error: E, pub(crate) source_code: C, } impl Diagnostic for WithSourceCode { fn code<'a>(&'a self) -> Option> { self.error.code() } fn severity(&self) -> Option { self.error.severity() } fn help<'a>(&'a self) -> Option> { self.error.help() } fn url<'a>(&'a self) -> Option> { self.error.url() } fn labels<'a>(&'a self) -> Option + 'a>> { self.error.labels() } fn source_code(&self) -> Option<&dyn miette::SourceCode> { self.error.source_code().or(Some(&self.source_code)) } fn related<'a>(&'a self) -> Option + 'a>> { self.error.related() } fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { self.error.diagnostic_source() } } impl Diagnostic for WithSourceCode { fn code<'a>(&'a self) -> Option> { self.error.code() } fn severity(&self) -> Option { self.error.severity() } fn help<'a>(&'a self) -> Option> { self.error.help() } fn url<'a>(&'a self) -> Option> { self.error.url() } fn labels<'a>(&'a self) -> Option + 'a>> { self.error.labels() } fn source_code(&self) -> Option<&dyn miette::SourceCode> { self.error.source_code().or(Some(&self.source_code)) } fn related<'a>(&'a self) -> Option + 'a>> { self.error.related() } fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { self.error.diagnostic_source() } } impl Debug for WithSourceCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.error, f) } } impl Display for WithSourceCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.error, f) } } impl StdError for WithSourceCode { fn source(&self) -> Option<&(dyn StdError + 'static)> { self.error.source() } } impl StdError for WithSourceCode { fn source(&self) -> Option<&(dyn StdError + 'static)> { self.error.source() } } #[cfg(test)] mod tests { use thiserror::Error; use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan}; #[derive(Error, Debug)] #[error("inner")] struct Inner { pub(crate) at: SourceSpan, pub(crate) source_code: Option, } impl Diagnostic for Inner { fn labels(&self) -> Option + '_>> { Some(Box::new(std::iter::once(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")] struct Outer { pub(crate) errors: Vec, } impl Diagnostic for Outer { fn related<'a>(&'a self) -> Option + 'a>> { Some(Box::new(self.errors.iter().map(|e| e as _))) } } #[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)); } }