Switch report handler to a global hook

This commit is contained in:
Jane Lusby 2020-06-21 11:43:36 -07:00
parent 96ddb39cd0
commit 608a16aa2c
9 changed files with 233 additions and 309 deletions

View File

@ -24,6 +24,7 @@ anyhow = "1.0.28"
[dependencies]
indenter = "0.3.0"
once_cell = "1.4.0"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,5 +1,5 @@
use crate::error::ContextError;
use crate::{ContextCompat, EyreHandler, Report, StdError, WrapErr};
use crate::{ContextCompat, Report, StdError, WrapErr};
use core::fmt::{self, Debug, Display, Write};
#[cfg(backtrace)]
@ -8,22 +8,18 @@ use std::backtrace::Backtrace;
mod ext {
use super::*;
pub trait StdError<H>
where
H: EyreHandler,
{
fn ext_report<D>(self, msg: D) -> Report<H>
pub trait StdError {
fn ext_report<D>(self, msg: D) -> Report
where
D: Display + Send + Sync + 'static;
}
#[cfg(feature = "std")]
impl<E, H> StdError<H> for E
impl<E> StdError for E
where
H: EyreHandler,
E: std::error::Error + Send + Sync + 'static,
{
fn ext_report<D>(self, msg: D) -> Report<H>
fn ext_report<D>(self, msg: D) -> Report
where
D: Display + Send + Sync + 'static,
{
@ -31,11 +27,8 @@ mod ext {
}
}
impl<H> StdError<H> for Report<H>
where
H: EyreHandler,
{
fn ext_report<D>(self, msg: D) -> Report<H>
impl StdError for Report {
fn ext_report<D>(self, msg: D) -> Report
where
D: Display + Send + Sync + 'static,
{
@ -44,19 +37,18 @@ mod ext {
}
}
impl<T, E, H> WrapErr<T, E, H> for Result<T, E>
impl<T, E> WrapErr<T, E> for Result<T, E>
where
H: EyreHandler,
E: ext::StdError<H> + Send + Sync + 'static,
E: ext::StdError + Send + Sync + 'static,
{
fn wrap_err<D>(self, msg: D) -> Result<T, Report<H>>
fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
{
self.map_err(|error| error.ext_report(msg))
}
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report<H>>
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D,
@ -64,14 +56,14 @@ where
self.map_err(|error| error.ext_report(msg()))
}
fn context<D>(self, msg: D) -> Result<T, Report<H>>
fn context<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
{
self.wrap_err(msg)
}
fn with_context<D, F>(self, msg: F) -> Result<T, Report<H>>
fn with_context<D, F>(self, msg: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D,
@ -80,18 +72,15 @@ where
}
}
impl<T, H> ContextCompat<T, H> for Option<T>
where
H: EyreHandler,
{
fn wrap_err<D>(self, msg: D) -> Result<T, Report<H>>
impl<T> ContextCompat<T> for Option<T> {
fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
{
self.context(msg)
}
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report<H>>
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D,
@ -99,14 +88,14 @@ where
self.with_context(msg)
}
fn context<D>(self, msg: D) -> Result<T, Report<H>>
fn context<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
{
self.ok_or_else(|| Report::from_display(msg))
}
fn with_context<D, F>(self, msg: F) -> Result<T, Report<H>>
fn with_context<D, F>(self, msg: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D,
@ -152,9 +141,8 @@ where
}
}
impl<D, H> StdError for ContextError<D, Report<H>>
impl<D> StdError for ContextError<D, Report>
where
H: EyreHandler,
D: Display,
{
fn source(&self) -> Option<&(dyn StdError + 'static)> {
@ -185,8 +173,8 @@ impl Write for Quoted<&mut fmt::Formatter<'_>> {
pub(crate) mod private {
use super::*;
pub trait Sealed<H: EyreHandler> {}
pub trait Sealed {}
impl<T, E, H: EyreHandler> Sealed<H> for Result<T, E> where E: ext::StdError<H> {}
impl<T, H: EyreHandler> Sealed<H> for Option<T> {}
impl<T, E> Sealed for Result<T, E> where E: ext::StdError {}
impl<T> Sealed for Option<T> {}
}

View File

@ -10,10 +10,7 @@ use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use core::ops::{Deref, DerefMut};
impl<H> Report<H>
where
H: EyreHandler,
{
impl Report {
/// Create a new error object from any error type.
///
/// The error type must be threadsafe and `'static`, so that the `Report`
@ -80,17 +77,18 @@ where
E: StdError + Send + Sync + 'static,
{
let vtable = &ErrorVTable {
object_drop: object_drop::<E, H>,
object_ref: object_ref::<E, H>,
object_drop: object_drop::<E>,
object_ref: object_ref::<E>,
#[cfg(feature = "std")]
object_mut: object_mut::<E, H>,
object_boxed: object_boxed::<E, H>,
object_downcast: object_downcast::<E, H>,
object_drop_rest: object_drop_front::<E, H>,
object_mut: object_mut::<E>,
object_boxed: object_boxed::<E>,
object_downcast: object_downcast::<E>,
object_drop_rest: object_drop_front::<E>,
};
// Safety: passing vtable that operates on the right type E.
let handler = Some(H::default(&error));
let handler = Some(crate::capture_handler(&error));
unsafe { Report::construct(error, vtable, handler) }
}
@ -101,18 +99,19 @@ where
use crate::wrapper::MessageError;
let error: MessageError<M> = MessageError(message);
let vtable = &ErrorVTable {
object_drop: object_drop::<MessageError<M>, H>,
object_ref: object_ref::<MessageError<M>, H>,
object_drop: object_drop::<MessageError<M>>,
object_ref: object_ref::<MessageError<M>>,
#[cfg(feature = "std")]
object_mut: object_mut::<MessageError<M>, H>,
object_boxed: object_boxed::<MessageError<M>, H>,
object_downcast: object_downcast::<M, H>,
object_drop_rest: object_drop_front::<M, H>,
object_mut: object_mut::<MessageError<M>>,
object_boxed: object_boxed::<MessageError<M>>,
object_downcast: object_downcast::<M>,
object_drop_rest: object_drop_front::<M>,
};
// Safety: MessageError is repr(transparent) so it is okay for the
// vtable to allow casting the MessageError<M> to M.
let handler = Some(H::default(&error));
let handler = Some(crate::capture_handler(&error));
unsafe { Report::construct(error, vtable, handler) }
}
@ -123,18 +122,19 @@ where
use crate::wrapper::{DisplayError, NoneError};
let error: DisplayError<M> = DisplayError(message);
let vtable = &ErrorVTable {
object_drop: object_drop::<DisplayError<M>, H>,
object_ref: object_ref::<DisplayError<M>, H>,
object_drop: object_drop::<DisplayError<M>>,
object_ref: object_ref::<DisplayError<M>>,
#[cfg(feature = "std")]
object_mut: object_mut::<DisplayError<M>, H>,
object_boxed: object_boxed::<DisplayError<M>, H>,
object_downcast: object_downcast::<M, H>,
object_drop_rest: object_drop_front::<M, H>,
object_mut: object_mut::<DisplayError<M>>,
object_boxed: object_boxed::<DisplayError<M>>,
object_downcast: object_downcast::<M>,
object_drop_rest: object_drop_front::<M>,
};
// Safety: DisplayError is repr(transparent) so it is okay for the
// vtable to allow casting the DisplayError<M> to M.
let handler = Some(H::default(&NoneError));
let handler = Some(crate::capture_handler(&NoneError));
unsafe { Report::construct(error, vtable, handler) }
}
@ -147,17 +147,18 @@ where
let error: ContextError<D, E> = ContextError { msg, error };
let vtable = &ErrorVTable {
object_drop: object_drop::<ContextError<D, E>, H>,
object_ref: object_ref::<ContextError<D, E>, H>,
object_drop: object_drop::<ContextError<D, E>>,
object_ref: object_ref::<ContextError<D, E>>,
#[cfg(feature = "std")]
object_mut: object_mut::<ContextError<D, E>, H>,
object_boxed: object_boxed::<ContextError<D, E>, H>,
object_downcast: context_downcast::<D, E, H>,
object_drop_rest: context_drop_rest::<D, E, H>,
object_mut: object_mut::<ContextError<D, E>>,
object_boxed: object_boxed::<ContextError<D, E>>,
object_downcast: context_downcast::<D, E>,
object_drop_rest: context_drop_rest::<D, E>,
};
// Safety: passing vtable that operates on the right type.
let handler = Some(H::default(&error));
let handler = Some(crate::capture_handler(&error));
unsafe { Report::construct(error, vtable, handler) }
}
@ -165,15 +166,16 @@ where
pub(crate) fn from_boxed(error: Box<dyn StdError + Send + Sync>) -> Self {
use crate::wrapper::BoxedError;
let error = BoxedError(error);
let handler = Some(H::default(&error));
let handler = Some(crate::capture_handler(&error));
let vtable = &ErrorVTable {
object_drop: object_drop::<BoxedError, H>,
object_ref: object_ref::<BoxedError, H>,
object_drop: object_drop::<BoxedError>,
object_ref: object_ref::<BoxedError>,
#[cfg(feature = "std")]
object_mut: object_mut::<BoxedError, H>,
object_boxed: object_boxed::<BoxedError, H>,
object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>, H>,
object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>, H>,
object_mut: object_mut::<BoxedError>,
object_boxed: object_boxed::<BoxedError>,
object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
};
// Safety: BoxedError is repr(transparent) so it is okay for the vtable
@ -186,7 +188,11 @@ where
//
// Unsafe because the given vtable must have sensible behavior on the error
// value of type E.
unsafe fn construct<E>(error: E, vtable: &'static ErrorVTable<H>, handler: Option<H>) -> Self
unsafe fn construct<E>(
error: E,
vtable: &'static ErrorVTable,
handler: Option<Box<dyn EyreHandler>>,
) -> Self
where
E: StdError + Send + Sync + 'static,
{
@ -201,7 +207,7 @@ where
// result is a thin pointer. The necessary behavior for manipulating the
// underlying ErrorImpl<E> is preserved in the vtable provided by the
// caller rather than a builtin fat pointer vtable.
let erased = mem::transmute::<Box<ErrorImpl<E, H>>, Box<ErrorImpl<(), H>>>(inner);
let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner);
let inner = ManuallyDrop::new(erased);
Report { inner }
}
@ -264,16 +270,16 @@ where
D: Display + Send + Sync + 'static,
{
let handler = self.inner.handler.take();
let error: ContextError<D, Report<H>> = ContextError { msg, error: self };
let error: ContextError<D, Report> = ContextError { msg, error: self };
let vtable = &ErrorVTable {
object_drop: object_drop::<ContextError<D, Report<H>>, H>,
object_ref: object_ref::<ContextError<D, Report<H>>, H>,
object_drop: object_drop::<ContextError<D, Report>>,
object_ref: object_ref::<ContextError<D, Report>>,
#[cfg(feature = "std")]
object_mut: object_mut::<ContextError<D, Report<H>>, H>,
object_boxed: object_boxed::<ContextError<D, Report<H>>, H>,
object_downcast: context_chain_downcast::<D, H>,
object_drop_rest: context_chain_drop_rest::<D, H>,
object_mut: object_mut::<ContextError<D, Report>>,
object_boxed: object_boxed::<ContextError<D, Report>>,
object_downcast: context_chain_downcast::<D>,
object_drop_rest: context_chain_drop_rest::<D>,
};
// Safety: passing vtable that operates on the right type.
@ -432,32 +438,31 @@ where
}
/// Get a reference to the Handler for this Report.
pub fn handler(&self) -> &H {
self.inner.handler.as_ref().unwrap()
pub fn handler(&self) -> &dyn EyreHandler {
self.inner.handler.as_ref().unwrap().as_ref()
}
/// Get a mutable reference to the Handler for this Report.
pub fn handler_mut(&mut self) -> &mut H {
self.inner.handler.as_mut().unwrap()
pub fn handler_mut(&mut self) -> &mut dyn EyreHandler {
self.inner.handler.as_mut().unwrap().as_mut()
}
/// Get a reference to the Handler for this Report.
#[doc(hidden)]
pub fn context(&self) -> &H {
self.inner.handler.as_ref().unwrap()
pub fn context(&self) -> &dyn EyreHandler {
self.inner.handler.as_ref().unwrap().as_ref()
}
/// Get a mutable reference to the Handler for this Report.
#[doc(hidden)]
pub fn context_mut(&mut self) -> &mut H {
self.inner.handler.as_mut().unwrap()
pub fn context_mut(&mut self) -> &mut dyn EyreHandler {
self.inner.handler.as_mut().unwrap().as_mut()
}
}
#[cfg(feature = "std")]
impl<E, H> From<E> for Report<H>
impl<E> From<E> for Report
where
H: EyreHandler,
E: StdError + Send + Sync + 'static,
{
fn from(error: E) -> Self {
@ -466,7 +471,7 @@ where
}
#[cfg(feature = "std")]
impl<H: EyreHandler> Deref for Report<H> {
impl Deref for Report {
type Target = dyn StdError + Send + Sync + 'static;
fn deref(&self) -> &Self::Target {
@ -475,28 +480,25 @@ impl<H: EyreHandler> Deref for Report<H> {
}
#[cfg(feature = "std")]
impl<H: EyreHandler> DerefMut for Report<H> {
impl DerefMut for Report {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.error_mut()
}
}
impl<H: EyreHandler> Display for Report<H> {
impl Display for Report {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.display(formatter)
}
}
impl<H: EyreHandler> Debug for Report<H> {
impl Debug for Report {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.debug(formatter)
}
}
impl<H> Drop for Report<H>
where
H: EyreHandler,
{
impl Drop for Report {
fn drop(&mut self) {
unsafe {
// Read Box<ErrorImpl<()>> from self.
@ -509,86 +511,72 @@ where
}
}
struct ErrorVTable<H>
where
H: EyreHandler,
{
object_drop: unsafe fn(Box<ErrorImpl<(), H>>),
object_ref: unsafe fn(&ErrorImpl<(), H>) -> &(dyn StdError + Send + Sync + 'static),
struct ErrorVTable {
object_drop: unsafe fn(Box<ErrorImpl<()>>),
object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static),
#[cfg(feature = "std")]
object_mut: unsafe fn(&mut ErrorImpl<(), H>) -> &mut (dyn StdError + Send + Sync + 'static),
object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static),
#[allow(clippy::type_complexity)]
object_boxed: unsafe fn(Box<ErrorImpl<(), H>>) -> Box<dyn StdError + Send + Sync + 'static>,
object_downcast: unsafe fn(&ErrorImpl<(), H>, TypeId) -> Option<NonNull<()>>,
object_drop_rest: unsafe fn(Box<ErrorImpl<(), H>>, TypeId),
object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>,
object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>,
object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId),
}
// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_drop<E, H>(e: Box<ErrorImpl<(), H>>)
where
H: EyreHandler,
{
unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) {
// Cast back to ErrorImpl<E> so that the allocator receives the correct
// Layout to deallocate the Box's memory.
let unerased = mem::transmute::<Box<ErrorImpl<(), H>>, Box<ErrorImpl<E, H>>>(e);
let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e);
drop(unerased);
}
// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_drop_front<E, H>(e: Box<ErrorImpl<(), H>>, target: TypeId)
where
H: EyreHandler,
{
unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) {
// Drop the fields of ErrorImpl other than E as well as the Box allocation,
// without dropping E itself. This is used by downcast after doing a
// ptr::read to take ownership of the E.
let _ = target;
let unerased = mem::transmute::<Box<ErrorImpl<(), H>>, Box<ErrorImpl<ManuallyDrop<E>, H>>>(e);
let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e);
drop(unerased);
}
// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_ref<E, H>(e: &ErrorImpl<(), H>) -> &(dyn StdError + Send + Sync + 'static)
unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static)
where
H: EyreHandler,
E: StdError + Send + Sync + 'static,
{
// Attach E's native StdError vtable onto a pointer to self._object.
&(*(e as *const ErrorImpl<(), H> as *const ErrorImpl<E, H>))._object
&(*(e as *const ErrorImpl<()> as *const ErrorImpl<E>))._object
}
// Safety: requires layout of *e to match ErrorImpl<E>.
#[cfg(feature = "std")]
unsafe fn object_mut<E, H>(e: &mut ErrorImpl<(), H>) -> &mut (dyn StdError + Send + Sync + 'static)
unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static)
where
H: EyreHandler,
E: StdError + Send + Sync + 'static,
{
// Attach E's native StdError vtable onto a pointer to self._object.
&mut (*(e as *mut ErrorImpl<(), H> as *mut ErrorImpl<E, H>))._object
&mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object
}
// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_boxed<E, H>(e: Box<ErrorImpl<(), H>>) -> Box<dyn StdError + Send + Sync + 'static>
unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>
where
H: EyreHandler,
E: StdError + Send + Sync + 'static,
H: Send + Sync + 'static,
{
// Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below.
mem::transmute::<Box<ErrorImpl<(), H>>, Box<ErrorImpl<E, H>>>(e)
mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e)
}
// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_downcast<E, H>(e: &ErrorImpl<(), H>, target: TypeId) -> Option<NonNull<()>>
unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
where
H: EyreHandler,
E: 'static,
{
if TypeId::of::<E>() == target {
// Caller is looking for an E pointer and e is ErrorImpl<E>, take a
// pointer to its E field.
let unerased = e as *const ErrorImpl<(), H> as *const ErrorImpl<E, H>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<E>;
let addr = &(*unerased)._object as *const E as *mut ();
Some(NonNull::new_unchecked(addr))
} else {
@ -598,18 +586,17 @@ where
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, E>>.
#[cfg(feature = "std")]
unsafe fn context_downcast<D, E, H>(e: &ErrorImpl<(), H>, target: TypeId) -> Option<NonNull<()>>
unsafe fn context_downcast<D, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
where
H: EyreHandler,
D: 'static,
E: 'static,
{
if TypeId::of::<D>() == target {
let unerased = e as *const ErrorImpl<(), H> as *const ErrorImpl<ContextError<D, E>, H>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, E>>;
let addr = &(*unerased)._object.msg as *const D as *mut ();
Some(NonNull::new_unchecked(addr))
} else if TypeId::of::<E>() == target {
let unerased = e as *const ErrorImpl<(), H> as *const ErrorImpl<ContextError<D, E>, H>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, E>>;
let addr = &(*unerased)._object.error as *const E as *mut ();
Some(NonNull::new_unchecked(addr))
} else {
@ -619,9 +606,8 @@ where
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, E>>.
#[cfg(feature = "std")]
unsafe fn context_drop_rest<D, E, H>(e: Box<ErrorImpl<(), H>>, target: TypeId)
unsafe fn context_drop_rest<D, E>(e: Box<ErrorImpl<()>>, target: TypeId)
where
H: EyreHandler,
D: 'static,
E: 'static,
{
@ -629,58 +615,54 @@ where
// ptr::read to take ownership of that value.
if TypeId::of::<D>() == target {
let unerased = mem::transmute::<
Box<ErrorImpl<(), H>>,
Box<ErrorImpl<ContextError<ManuallyDrop<D>, E>, H>>,
Box<ErrorImpl<()>>,
Box<ErrorImpl<ContextError<ManuallyDrop<D>, E>>>,
>(e);
drop(unerased);
} else {
let unerased = mem::transmute::<
Box<ErrorImpl<(), H>>,
Box<ErrorImpl<ContextError<D, ManuallyDrop<E>>, H>>,
Box<ErrorImpl<()>>,
Box<ErrorImpl<ContextError<D, ManuallyDrop<E>>>>,
>(e);
drop(unerased);
}
}
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
unsafe fn context_chain_downcast<D, H>(e: &ErrorImpl<(), H>, target: TypeId) -> Option<NonNull<()>>
unsafe fn context_chain_downcast<D>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
where
H: EyreHandler,
D: 'static,
{
if TypeId::of::<D>() == target {
let unerased =
e as *const ErrorImpl<(), H> as *const ErrorImpl<ContextError<D, Report<H>>, H>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, Report>>;
let addr = &(*unerased)._object.msg as *const D as *mut ();
Some(NonNull::new_unchecked(addr))
} else {
// Recurse down the context chain per the inner error's vtable.
let unerased =
e as *const ErrorImpl<(), H> as *const ErrorImpl<ContextError<D, Report<H>>, H>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, Report>>;
let source = &(*unerased)._object.error;
(source.inner.vtable.object_downcast)(&source.inner, target)
}
}
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
unsafe fn context_chain_drop_rest<D, H>(e: Box<ErrorImpl<(), H>>, target: TypeId)
unsafe fn context_chain_drop_rest<D>(e: Box<ErrorImpl<()>>, target: TypeId)
where
H: EyreHandler,
D: 'static,
{
// Called after downcasting by value to either the D or one of the causes
// and doing a ptr::read to take ownership of that value.
if TypeId::of::<D>() == target {
let unerased = mem::transmute::<
Box<ErrorImpl<(), H>>,
Box<ErrorImpl<ContextError<ManuallyDrop<D>, Report<H>>, H>>,
Box<ErrorImpl<()>>,
Box<ErrorImpl<ContextError<ManuallyDrop<D>, Report>>>,
>(e);
// Drop the entire rest of the data structure rooted in the next Report.
drop(unerased);
} else {
let unerased = mem::transmute::<
Box<ErrorImpl<(), H>>,
Box<ErrorImpl<ContextError<D, ManuallyDrop<Report<H>>>, H>>,
Box<ErrorImpl<()>>,
Box<ErrorImpl<ContextError<D, ManuallyDrop<Report>>>>,
>(e);
// Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error.
let inner = ptr::read(&unerased._object.error.inner);
@ -693,12 +675,9 @@ where
// repr C to ensure that E remains in the final position.
#[repr(C)]
pub(crate) struct ErrorImpl<E, H>
where
H: EyreHandler,
{
vtable: &'static ErrorVTable<H>,
pub(crate) handler: Option<H>,
pub(crate) struct ErrorImpl<E> {
vtable: &'static ErrorVTable,
pub(crate) handler: Option<Box<dyn EyreHandler>>,
// NOTE: Don't use directly. Use only through vtable. Erased type may have
// different alignment.
_object: E,
@ -712,22 +691,16 @@ pub(crate) struct ContextError<D, E> {
pub(crate) error: E,
}
impl<E, H> ErrorImpl<E, H>
where
H: EyreHandler,
{
fn erase(&self) -> &ErrorImpl<(), H> {
impl<E> ErrorImpl<E> {
fn erase(&self) -> &ErrorImpl<()> {
// Erase the concrete type of E but preserve the vtable in self.vtable
// for manipulating the resulting thin pointer. This is analogous to an
// unsize coersion.
unsafe { &*(self as *const ErrorImpl<E, H> as *const ErrorImpl<(), H>) }
unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) }
}
}
impl<H> ErrorImpl<(), H>
where
H: EyreHandler,
{
impl ErrorImpl<()> {
pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) {
// Use vtable to attach E's native StdError vtable for the right
// original type E.
@ -746,9 +719,8 @@ where
}
}
impl<E, H> StdError for ErrorImpl<E, H>
impl<E> StdError for ErrorImpl<E>
where
H: EyreHandler,
E: StdError,
{
fn source(&self) -> Option<&(dyn StdError + 'static)> {
@ -756,9 +728,8 @@ where
}
}
impl<E, H> Debug for ErrorImpl<E, H>
impl<E> Debug for ErrorImpl<E>
where
H: EyreHandler,
E: Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -766,9 +737,8 @@ where
}
}
impl<E, H> Display for ErrorImpl<E, H>
impl<E> Display for ErrorImpl<E>
where
H: EyreHandler,
E: Display,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -776,8 +746,8 @@ where
}
}
impl<H: EyreHandler> From<Report<H>> for Box<dyn StdError + Send + Sync + 'static> {
fn from(error: Report<H>) -> Self {
impl From<Report> for Box<dyn StdError + Send + Sync + 'static> {
fn from(error: Report) -> Self {
let outer = ManuallyDrop::new(error);
unsafe {
// Read Box<ErrorImpl<()>> from error. Can't move it out because
@ -792,21 +762,21 @@ impl<H: EyreHandler> From<Report<H>> for Box<dyn StdError + Send + Sync + 'stati
}
}
impl<H: EyreHandler> From<Report<H>> for Box<dyn StdError + 'static> {
fn from(error: Report<H>) -> Self {
impl From<Report> for Box<dyn StdError + 'static> {
fn from(error: Report) -> Self {
Box::<dyn StdError + Send + Sync>::from(error)
}
}
#[cfg(feature = "std")]
impl<H: EyreHandler> AsRef<dyn StdError + Send + Sync> for Report<H> {
impl AsRef<dyn StdError + Send + Sync> for Report {
fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
&**self
}
}
#[cfg(feature = "std")]
impl<H: EyreHandler> AsRef<dyn StdError> for Report<H> {
impl AsRef<dyn StdError> for Report {
fn as_ref(&self) -> &(dyn StdError + 'static) {
&**self
}

View File

@ -1,11 +1,7 @@
use crate::error::ErrorImpl;
use crate::EyreHandler;
use core::fmt;
impl<H> ErrorImpl<(), H>
where
H: EyreHandler,
{
impl ErrorImpl<()> {
pub(crate) fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.handler
.as_ref()

View File

@ -45,7 +45,7 @@
// let error = $msg;
// (&error).eyre_kind().new(error)
use crate::{EyreHandler, Report};
use crate::Report;
use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
@ -63,7 +63,7 @@ pub trait AdhocKind: Sized {
impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
impl Adhoc {
pub fn new<M, H: EyreHandler>(self, message: M) -> Report<H>
pub fn new<M>(self, message: M) -> Report
where
M: Display + Debug + Send + Sync + 'static,
{
@ -71,27 +71,21 @@ impl Adhoc {
}
}
pub struct Trait<H>(std::marker::PhantomData<H>);
pub struct Trait;
pub trait TraitKind<H>: Sized {
pub trait TraitKind: Sized {
#[inline]
fn eyre_kind(&self) -> Trait<H> {
Trait(std::marker::PhantomData)
fn eyre_kind(&self) -> Trait {
Trait
}
}
impl<E, H> TraitKind<H> for E
where
E: Into<Report<H>>,
H: EyreHandler,
{
}
impl<E> TraitKind for E where E: Into<Report> {}
impl<H> Trait<H> {
pub fn new<E>(self, error: E) -> Report<H>
impl Trait {
pub fn new<E>(self, error: E) -> Report
where
E: Into<Report<H>>,
H: EyreHandler,
E: Into<Report>,
{
error.into()
}
@ -113,7 +107,7 @@ impl BoxedKind for Box<dyn StdError + Send + Sync> {}
#[cfg(feature = "std")]
impl Boxed {
pub fn new<H: EyreHandler>(self, error: Box<dyn StdError + Send + Sync>) -> Report<H> {
pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Report {
Report::from_boxed(error)
}
}
@ -122,16 +116,12 @@ impl Boxed {
mod test {
use super::*;
use crate::eyre;
use crate::EyreHandler;
use std::num::ParseIntError;
struct NonDefaultHandler;
impl EyreHandler for NonDefaultHandler {
#[allow(unused_variables)]
fn default(error: &(dyn StdError + 'static)) -> Self {
Self
}
fn debug(
&self,
_error: &(dyn StdError + 'static),
@ -145,7 +135,7 @@ mod test {
s.parse::<i32>()
}
fn _throw_error() -> Result<(), Report<NonDefaultHandler>> {
fn _throw_error() -> Result<(), Report> {
match _parse("abc") {
Ok(_) => Ok(()),
Err(e) => Err(eyre!(e).wrap_err("try parsing an actual number")),

View File

@ -399,6 +399,7 @@ pub trait StdError: Debug + Display {
pub use eyre as format_err;
/// Compatibility re-export of `eyre` for interopt with `anyhow`
pub use eyre as anyhow;
use once_cell::sync::OnceCell;
#[doc(hidden)]
pub use DefaultHandler as DefaultContext;
#[doc(hidden)]
@ -499,11 +500,55 @@ pub use WrapErr as Context;
/// # Ok(())
/// }
/// ```
pub struct Report<H = DefaultHandler>
where
H: EyreHandler,
{
inner: ManuallyDrop<Box<ErrorImpl<(), H>>>,
pub struct Report {
inner: ManuallyDrop<Box<ErrorImpl<()>>>,
}
type ErrorHook =
Box<dyn Fn(&(dyn StdError + 'static)) -> Box<dyn EyreHandler> + Sync + Send + 'static>;
static HOOK: OnceCell<ErrorHook> = OnceCell::new();
///
pub fn set_hook(hook: ErrorHook) -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
HOOK.set(hook)
.map_err(|_| "unable to set global hook".into())
}
fn capture_handler(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
HOOK.get_or_init(|| Box::new(DefaultHandler::default_with))(error)
}
impl dyn EyreHandler {
///
pub fn is<T: EyreHandler>(&self) -> bool {
// Get `TypeId` of the type this function is instantiated with.
let t = core::any::TypeId::of::<T>();
// Get `TypeId` of the type in the trait object (`self`).
let concrete = self.type_id();
// Compare both `TypeId`s on equality.
t == concrete
}
///
pub fn downcast_ref<T: EyreHandler>(&self) -> Option<&T> {
if self.is::<T>() {
unsafe { Some(&*(self as *const dyn EyreHandler as *const T)) }
} else {
None
}
}
///
pub fn downcast_mut<T: EyreHandler>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
unsafe { Some(&mut *(self as *mut dyn EyreHandler as *mut T)) }
} else {
None
}
}
}
/// Error Report Handler trait for customizing `eyre::Report`
@ -565,64 +610,7 @@ where
/// type Report = eyre::Report<Handler>;
/// type Result<T, E = eyre::Report<Handler>> = core::result::Result<T, E>;
/// ```
pub trait EyreHandler: Sized + Send + Sync + 'static {
/// Default construct a `Handler` when constructing a `Report`.
///
/// This method provides a reference to the error being wrapped to support conditional
/// capturing of context like `backtrace` depending on whether the source error already
/// captured one.
///
/// # Example
///
/// ```rust
/// use backtrace::Backtrace;
/// use eyre::EyreHandler;
/// # use eyre::Chain;
/// use std::error::Error;
///
/// pub struct Handler {
/// backtrace: Backtrace,
/// }
///
/// impl EyreHandler for Handler {
/// # #[allow(unused_variables)]
/// fn default(error: &(dyn Error + 'static)) -> Self {
/// let backtrace = Backtrace::new();
///
/// Self { backtrace }
/// }
///
/// // ...
/// # fn debug(
/// # &self,
/// # error: &(dyn Error + 'static),
/// # f: &mut core::fmt::Formatter<'_>,
/// # ) -> core::fmt::Result {
/// # use core::fmt::Write as _;
/// # if f.alternate() {
/// # return core::fmt::Debug::fmt(error, f);
/// # }
/// # write!(f, "{}", error)?;
/// # if let Some(cause) = error.source() {
/// # write!(f, "\n\nCaused by:")?;
/// # let multiple = cause.source().is_some();
/// # for (n, error) in Chain::new(cause).enumerate() {
/// # writeln!(f)?;
/// # if multiple {
/// # write!(indenter::indented(f).ind(n), "{}", error)?;
/// # } else {
/// # write!(indenter::indented(f), "{}", error)?;
/// # }
/// # }
/// # }
/// # let backtrace = &self.backtrace;
/// # write!(f, "\n\nStack backtrace:\n{:?}", backtrace)?;
/// # Ok(())
/// # }
/// }
/// ```
fn default(err: &(dyn StdError + 'static)) -> Self;
pub trait EyreHandler: core::any::Any + Send + Sync {
/// Define the report format
///
/// Used to override the report format of `eyre::Report`
@ -715,6 +703,15 @@ pub struct DefaultHandler {
backtrace: Option<Backtrace>,
}
impl DefaultHandler {
#[allow(unused_variables)]
fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
let backtrace = backtrace_if_absent!(error);
Box::new(Self { backtrace })
}
}
impl core::fmt::Debug for DefaultHandler {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DefaultHandler")
@ -730,13 +727,6 @@ impl core::fmt::Debug for DefaultHandler {
}
impl EyreHandler for DefaultHandler {
#[allow(unused_variables)]
fn default(error: &(dyn StdError + 'static)) -> Self {
let backtrace = backtrace_if_absent!(error);
Self { backtrace }
}
fn debug(
&self,
error: &(dyn StdError + 'static),
@ -855,7 +845,7 @@ pub struct Chain<'a> {
/// Ok(())
/// }
/// ```
pub type Result<T, E = Report<DefaultHandler>> = core::result::Result<T, E>;
pub type Result<T, E = Report> = core::result::Result<T, E>;
/// Provides the `wrap_err` method for `Result`.
///
@ -1022,29 +1012,26 @@ pub type Result<T, E = Report<DefaultHandler>> = core::result::Result<T, E>;
/// # panic!("expected downcast to succeed");
/// }
/// ```
pub trait WrapErr<T, E, H>: context::private::Sealed<H>
where
H: EyreHandler,
{
pub trait WrapErr<T, E>: context::private::Sealed {
/// Wrap the error value with a new adhoc error
fn wrap_err<D>(self, msg: D) -> Result<T, Report<H>>
fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static;
/// Wrap the error value with a new adhoc error that is evaluated lazily
/// only once an error does occur.
fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report<H>>
fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
/// Compatibility re-export of wrap_err for interopt with `anyhow`
fn context<D>(self, msg: D) -> Result<T, Report<H>>
fn context<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static;
/// Compatibility re-export of wrap_err_with for interopt with `anyhow`
fn with_context<D, F>(self, f: F) -> Result<T, Report<H>>
fn with_context<D, F>(self, f: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
@ -1093,30 +1080,27 @@ where
/// .ok_or_else(|| eyre!("the thing wasnt in the list"))
/// }
/// ```
pub trait ContextCompat<T, H>: context::private::Sealed<H>
where
H: EyreHandler,
{
pub trait ContextCompat<T>: context::private::Sealed {
/// Compatibility version of `wrap_err` for creating new errors with new source on `Option`
/// when porting from `anyhow`
fn context<D>(self, msg: D) -> Result<T, Report<H>>
fn context<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static;
/// Compatibility version of `wrap_err_with` for creating new errors with new source on `Option`
/// when porting from `anyhow`
fn with_context<D, F>(self, f: F) -> Result<T, Report<H>>
fn with_context<D, F>(self, f: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
/// Compatibility re-export of `context` for porting from `anyhow` to `eyre`
fn wrap_err<D>(self, msg: D) -> Result<T, Report<H>>
fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static;
/// Compatibility re-export of `with_context` for porting from `anyhow` to `eyre`
fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report<H>>
fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
@ -1125,12 +1109,9 @@ where
// Not public API. Referenced by macro-generated code.
#[doc(hidden)]
pub mod private {
use crate::{EyreHandler, Report};
use crate::Report;
use core::fmt::{Debug, Display};
// #[cfg(backtrace)]
// use std::backtrace::Backtrace;
pub use core::result::Result::Err;
#[doc(hidden)]
@ -1141,9 +1122,8 @@ pub mod private {
pub use crate::kind::BoxedKind;
}
pub fn new_adhoc<M, H>(message: M) -> Report<H>
pub fn new_adhoc<M>(message: M) -> Report
where
H: EyreHandler,
M: Display + Debug + Send + Sync + 'static,
{
Report::from_adhoc(message)

View File

@ -3,7 +3,7 @@ mod drop;
use self::common::*;
use self::drop::{DetectDrop, Flag};
use eyre::{DefaultContext, Report};
use eyre::Report;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io;
@ -90,7 +90,7 @@ fn test_large_alignment() {
impl StdError for LargeAlignedError {}
let error: Report<DefaultContext> = Report::new(LargeAlignedError("oh no!"));
let error = Report::new(LargeAlignedError("oh no!"));
assert_eq!(
"oh no!",
error.downcast_ref::<LargeAlignedError>().unwrap().0

View File

@ -1,7 +1,6 @@
mod drop;
use self::drop::{DetectDrop, Flag};
use eyre::DefaultContext;
use eyre::Report;
use std::marker::Unpin;
use std::mem;
@ -28,6 +27,6 @@ fn test_autotraits() {
#[test]
fn test_drop() {
let has_dropped = Flag::new();
drop(Report::<DefaultContext>::new(DetectDrop::new(&has_dropped)));
drop(Report::new(DetectDrop::new(&has_dropped)));
assert!(has_dropped.get());
}

View File

@ -33,7 +33,7 @@ fn test_literal_source() {
#[test]
fn test_variable_source() {
let msg = "oh no!";
let error: Report = eyre!(msg);
let error = eyre!(msg);
assert!(error.source().is_none());
let msg = msg.to_owned();