use crate::SpanTrace;
use std::error::Error;
use std::fmt::{self, Debug, Display};
struct Erased;
#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))]
pub struct TracedError<E> {
inner: ErrorImpl<E>,
}
impl<E> From<E> for TracedError<E>
where
E: Error + Send + Sync + 'static,
{
fn from(error: E) -> Self {
let vtable = &ErrorVTable {
object_ref: object_ref::<E>,
};
Self {
inner: ErrorImpl {
vtable,
span_trace: SpanTrace::capture(),
error,
},
}
}
}
#[repr(C)]
struct ErrorImpl<E> {
vtable: &'static ErrorVTable,
span_trace: SpanTrace,
error: E,
}
impl ErrorImpl<Erased> {
pub(crate) fn error(&self) -> &(dyn Error + Send + Sync + 'static) {
unsafe { &*(self.vtable.object_ref)(self) }
}
}
struct ErrorVTable {
object_ref: unsafe fn(&ErrorImpl<Erased>) -> &(dyn Error + Send + Sync + 'static),
}
unsafe fn object_ref<E>(e: &ErrorImpl<Erased>) -> &(dyn Error + Send + Sync + 'static)
where
E: Error + Send + Sync + 'static,
{
&(*(e as *const ErrorImpl<Erased> as *const ErrorImpl<E>)).error
}
impl<E> Error for TracedError<E>
where
E: std::error::Error + 'static,
{
fn source<'a>(&'a self) -> Option<&'a (dyn Error + 'static)> {
let erased = unsafe { &*(&self.inner as *const ErrorImpl<E> as *const ErrorImpl<Erased>) };
Some(erased)
}
}
impl<E> Debug for TracedError<E>
where
E: std::error::Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.inner.error, f)
}
}
impl<E> Display for TracedError<E>
where
E: std::error::Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.inner.error, f)
}
}
impl Error for ErrorImpl<Erased> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.error().source()
}
}
impl Debug for ErrorImpl<Erased> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("span backtrace:\n")?;
Debug::fmt(&self.span_trace, f)
}
}
impl Display for ErrorImpl<Erased> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("span backtrace:\n")?;
Display::fmt(&self.span_trace, f)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))]
pub trait InstrumentError {
type Instrumented;
fn in_current_span(self) -> Self::Instrumented;
}
#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))]
pub trait InstrumentResult<T> {
type Instrumented;
fn in_current_span(self) -> Result<T, Self::Instrumented>;
}
impl<T, E> InstrumentResult<T> for Result<T, E>
where
E: InstrumentError,
{
type Instrumented = <E as InstrumentError>::Instrumented;
fn in_current_span(self) -> Result<T, Self::Instrumented> {
self.map_err(E::in_current_span)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))]
pub trait ExtractSpanTrace {
fn span_trace(&self) -> Option<&SpanTrace>;
}
impl<E> InstrumentError for E
where
TracedError<E>: From<E>,
{
type Instrumented = TracedError<E>;
fn in_current_span(self) -> Self::Instrumented {
TracedError::from(self)
}
}
impl ExtractSpanTrace for dyn Error + 'static {
fn span_trace(&self) -> Option<&SpanTrace> {
self.downcast_ref::<ErrorImpl<Erased>>()
.map(|inner| &inner.span_trace)
}
}