use std::borrow::Cow; #[cfg(feature = "backtrace")] use std::backtrace::BacktraceStatus; use std::fmt; use std::panic::Location; use std::sync::Arc; use crate::ReportOpts; use crate::report::report_write; /// Provides context furthering the explanation of *how* you got to an error. #[derive(Debug)] #[cfg_attr(any(feature = "arc-backtrace", feature = "clone-with-caveats"), derive(Clone))] pub struct Context { pub(crate) detail: Detail, pub(crate) extra: Vec, } impl Context { pub(crate) fn new(detail: Detail) -> Self { Self { detail, extra: Vec::new() } } pub fn detail(&self) -> &Detail { &self.detail } pub fn extra(&self) -> &[Detail] { &self.extra } pub fn detail_mut(&mut self) -> &mut Detail { &mut self.detail } pub fn extra_mut(&mut self) -> &mut [Detail] { &mut self.extra } pub fn pop_extra(&mut self) -> Option { self.extra.pop() } } impl fmt::Display for Context { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.detail.fmt(f)?; let opts = ReportOpts::default(); for detail in &self.extra { f.write_str("\n")?; report_write!(f, &opts.indent().next(), "- {detail}")?; } Ok(()) } } #[derive(Debug)] #[cfg_attr(any(feature = "arc-backtrace", feature = "clone-with-caveats"), derive(Clone))] #[non_exhaustive] pub enum Detail { Str(&'static str), String(String), Location(Location<'static>), #[cfg(feature = "backtrace")] Backtrace(PrivateBacktrace), Error(PrivateError), } impl Detail { #[cfg(feature = "backtrace")] #[track_caller] pub fn backtrace() -> Self { use std::backtrace::BacktraceStatus::*; let bt = std::backtrace::Backtrace::capture(); let bt = match bt.status() { Disabled => Backtrace::Disabled, Unsupported => Backtrace::Unsupported, _ => Backtrace::Other(bt.into()), }; Self::Backtrace(PrivateBacktrace(bt)) } } #[derive(Debug, Clone)] pub struct PrivateError(pub(crate) Arc); #[cfg(feature = "backtrace")] #[derive(Debug)] #[cfg_attr(any(feature = "arc-backtrace", feature = "clone-with-caveats"), derive(Clone))] pub struct PrivateBacktrace(pub(crate) Backtrace); // will be replaced with std::backtrace::Backtrace if and when it is Clone #[cfg(feature = "backtrace")] #[derive(Debug)] #[cfg_attr(feature = "arc-backtrace", derive(Clone))] pub(crate) enum Backtrace { Disabled, Unsupported, #[cfg(feature = "arc-backtrace")] Other(Arc), #[cfg(not(feature = "arc-backtrace"))] Other(std::backtrace::Backtrace), } #[cfg(all(feature = "backtrace", feature = "clone-with-caveats", not(feature = "arc-backtrace")))] impl Clone for Backtrace { fn clone(&self) -> Self { match self { Self::Unsupported => Self::Unsupported, _ => Self::Disabled } } } impl fmt::Display for Detail { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Str(s) => f.write_str(s), Self::String(s) => f.write_str(s), Self::Location(l) => write!(f, "at {l}"), #[cfg(feature = "backtrace")] Self::Backtrace(PrivateBacktrace(Backtrace::Unsupported)) => f.write_str("I'd like to show you a backtrace,\n but it's not supported on your platform"), #[cfg(feature = "backtrace")] Self::Backtrace(PrivateBacktrace(Backtrace::Disabled)) => f.write_str("If you'd like a backtrace,\n try again with RUST_BACKTRACE=1"), #[cfg(feature = "backtrace")] Self::Backtrace(PrivateBacktrace(Backtrace::Other(bt))) => { f.write_str(if bt.status() == BacktraceStatus::Captured { "Here is the backtrace:\n" } else { "I can't tell if backtraces are working,\n but I'll give it a go:\n" })?; write!(f, "{}", bt) }, Self::Error(PrivateError(e)) => e.fmt(f), } } } pub trait IntoContext: Sized { fn into_context(self) -> Context; #[inline(always)] fn with(self, other: impl IntoContext) -> Context { self.into_context().with(other) } #[inline(always)] #[track_caller] fn with_caller(self) -> Context { self.with(Location::caller()) } } impl IntoContext for Context { #[inline(always)] fn into_context(self) -> Context { self } /// Chains another piece of context that is a child from a hierarchical perspective. #[track_caller] #[inline] fn with(mut self, other: impl IntoContext) -> Self { let mut other = other.into_context(); self.extra.reserve(1 + other.extra.len()); self.extra.push(other.detail); self.extra.append(&mut other.extra); self } } impl IntoContext for String { #[inline(always)] fn into_context(self) -> Context { Context::new(Detail::String(self)) } } impl IntoContext for &'static str { #[inline(always)] fn into_context(self) -> Context { Context::new(Detail::Str(self)) } } impl IntoContext for Cow<'static, str> { #[inline] fn into_context(self) -> Context { match self { Cow::Borrowed(s) => s.into_context(), Cow::Owned(s) => s.into_context(), } } } impl<'a> IntoContext for &'a Location<'static> { #[inline] fn into_context(self) -> Context { Location::into_context(*self) } } impl IntoContext for Location<'static> { #[inline] fn into_context(self) -> Context { Context::new(Detail::Location(self)) } } impl IntoContext for Arc where E: std::error::Error + Send + Sync + 'static, { #[inline] fn into_context(self) -> Context { Context::new(Detail::Error(PrivateError(self))) } } impl IntoContext for Arc { #[inline] fn into_context(self) -> Context { Context::new(Detail::Error(PrivateError(self))) } } impl IntoContext for F where C: IntoContext, F: FnOnce() -> C, { #[inline(always)] fn into_context(self) -> Context { self().into_context() } }