use crate::*; use crate::report::report_write; /// Does not implement [`std::error::Error`] to allow a [`From`] implementation for all other error types. pub struct How(Box); struct HowInner { /// When true, the error will cause branchers to abort. classified: bool, #[cfg(feature = "backtrace")] backtrace: std::backtrace::Backtrace, // TODO: consider storing this vec inline (sharing the allocation with rest of the struct. // Probably move after `backtrace`) context: Vec, } impl How { #[must_use] #[inline(never)] #[track_caller] pub fn new(context: impl IntoContext) -> Self { Self(Box::new(HowInner { classified: false, context: { let mut vec = Vec::with_capacity(4); vec.push(format!("At {}", std::panic::Location::caller()).into_context()); vec.push(context.into_context()); vec }, #[cfg(feature = "backtrace")] backtrace: std::backtrace::Backtrace::capture(), })) } #[must_use] pub fn clone_without_backtrace(&self) -> Self { Self(Box::new(HowInner { classified: self.0.classified, context: self.0.context.clone(), #[cfg(feature = "backtrace")] backtrace: std::backtrace::Backtrace::disabled(), })) } #[inline] #[must_use] pub fn classified(mut self) -> Self { self.0.classified = true; self } #[inline] #[must_use] pub const fn is_classified(&self) -> bool { self.0.classified } fn fmt_debug_alternate(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut b = f.debug_struct(std::any::type_name::()); let b = b .field("classified", &self.0.classified) .field("context", &(&self.0.context)); #[cfg(feature = "backtrace")] let b = b.field("backtrace", &self.0.backtrace); b.finish() } } impl explain::Sealed for How {} impl Explain for How { type Output = Self; #[inline(always)] #[must_use] fn context(mut self, context: impl IntoContext) -> Self { self.0.context.push(context.into_context()); self } } impl std::fmt::Display for How { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut opts = ReportOpts::default(); let mut ctxs = self.0.context.iter().rev(); let ctx = ctxs.next().expect("`How` created with no context."); report_write!(f, &opts, "{ctx}")?; for ctx in ctxs { report_write!(f, &opts, "\n└ ")?; report_write!(f, &opts.indent().next(), "{ctx}")?; opts = opts.indent(); } #[cfg(feature = "backtrace")] { opts = opts.indent(); report_write!(f, &opts, "\n{}", self.0.backtrace)?; } Ok(()) } } impl std::fmt::Debug for How { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { self.fmt_debug_alternate(f) } else { std::fmt::Display::fmt(self, f) } } }