use std::panic::Location; use std::sync::Arc; use crate::{How, IntoContext, Context, ContextInner, 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(); let loc = Detail::Location(*Location::caller()); #[cfg(feature = "backtrace")] let bt = { 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) }; match context.0 { ContextInner::Elem(elem) => { #[cfg(not(feature = "backtrace"))] let vec = vec![elem, loc]; #[cfg(feature = "backtrace")] let vec = vec![elem, loc, bt]; context.0 = ContextInner::Compound(vec); } ContextInner::Compound(ref mut vec) => { vec.insert(1, loc); } } 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(ContextInner::Elem(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)), } } }