use std::panic::Location; use std::sync::Arc; use crate::{How, IntoContext, Context, Detail}; #[cfg(feature = "backtrace")] use crate::context::Backtrace; pub trait Explain { type Output; #[track_caller] #[must_use] fn context(self, context: impl IntoContext) -> Self::Output; } impl Explain for How { type Output = Self; #[track_caller] #[inline] fn context(mut self, context: impl IntoContext) -> Self { let mut context = context.into_context(); context.extra.reserve(if cfg!(feature = "backtrace") { 2 } else { 1 }); context.extra.push(Detail::Location(*Location::caller())); #[cfg(feature = "backtrace")] context.extra.push({ use std::backtrace::BacktraceStatus::*; let bt = std::backtrace::Backtrace::capture(); let bt = match bt.status() { Disabled => Backtrace::Disabled, Unsupported => Backtrace::Unsupported, status => Backtrace::Other(status, bt.to_string()), }; Detail::Backtrace(bt) }); self.push_context(context); self } } impl Explain for Result where E: std::error::Error + 'static, { type Output = Result; #[inline(always)] #[track_caller] fn context(self, context: impl IntoContext) -> Self::Output { #[cold] #[track_caller] fn into_and_context(e: E, c: C) -> How where E: std::error::Error + 'static, C: IntoContext, { match typeid_cast::cast(e) { Ok(e) => e, // TODO: specialize on Send + Sync at runtime or compile time (possibly via // specialization) //Err(e) => How::new(Context(ContextInner::Elem(Detail::Error(Arc::new(e))))), Err(e) => How::new(e.to_string()), } .context(c) } match self { Ok(t) => Ok(t), Err(e) => Err(into_and_context(e, context)), } } } impl Explain for Arc where E: std::error::Error + Send + Sync + 'static, { type Output = How; #[track_caller] fn context(self, context: impl IntoContext) -> Self::Output { How::new(Context::new(Detail::Error(self))) .context(context) } } impl Explain for Option { type Output = Result; #[inline(always)] #[track_caller] fn context(self, context: impl IntoContext) -> Self::Output { match self { Some(t) => Ok(t), None => Err(How::new(context)), } } }