From 608a16aa2c2c27eca6c88001cc94c6973c18f1d5 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sun, 21 Jun 2020 11:43:36 -0700 Subject: [PATCH] Switch report handler to a global hook --- Cargo.toml | 1 + src/context.rs | 56 ++++----- src/error.rs | 268 ++++++++++++++++++----------------------- src/fmt.rs | 6 +- src/kind.rs | 36 ++---- src/lib.rs | 166 +++++++++++-------------- tests/test_downcast.rs | 4 +- tests/test_repr.rs | 3 +- tests/test_source.rs | 2 +- 9 files changed, 233 insertions(+), 309 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23ae22b..4651e2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/context.rs b/src/context.rs index 608682f..a79556c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -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 - where - H: EyreHandler, - { - fn ext_report(self, msg: D) -> Report + pub trait StdError { + fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static; } #[cfg(feature = "std")] - impl StdError for E + impl StdError for E where - H: EyreHandler, E: std::error::Error + Send + Sync + 'static, { - fn ext_report(self, msg: D) -> Report + fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static, { @@ -31,11 +27,8 @@ mod ext { } } - impl StdError for Report - where - H: EyreHandler, - { - fn ext_report(self, msg: D) -> Report + impl StdError for Report { + fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static, { @@ -44,19 +37,18 @@ mod ext { } } -impl WrapErr for Result +impl WrapErr for Result where - H: EyreHandler, - E: ext::StdError + Send + Sync + 'static, + E: ext::StdError + Send + Sync + 'static, { - fn wrap_err(self, msg: D) -> Result> + fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { self.map_err(|error| error.ext_report(msg)) } - fn wrap_err_with(self, msg: F) -> Result> + fn wrap_err_with(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, @@ -64,14 +56,14 @@ where self.map_err(|error| error.ext_report(msg())) } - fn context(self, msg: D) -> Result> + fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { self.wrap_err(msg) } - fn with_context(self, msg: F) -> Result> + fn with_context(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, @@ -80,18 +72,15 @@ where } } -impl ContextCompat for Option -where - H: EyreHandler, -{ - fn wrap_err(self, msg: D) -> Result> +impl ContextCompat for Option { + fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { self.context(msg) } - fn wrap_err_with(self, msg: F) -> Result> + fn wrap_err_with(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, @@ -99,14 +88,14 @@ where self.with_context(msg) } - fn context(self, msg: D) -> Result> + fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { self.ok_or_else(|| Report::from_display(msg)) } - fn with_context(self, msg: F) -> Result> + fn with_context(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, @@ -152,9 +141,8 @@ where } } -impl StdError for ContextError> +impl StdError for ContextError 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 {} + pub trait Sealed {} - impl Sealed for Result where E: ext::StdError {} - impl Sealed for Option {} + impl Sealed for Result where E: ext::StdError {} + impl Sealed for Option {} } diff --git a/src/error.rs b/src/error.rs index 029843c..ddc1045 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,10 +10,7 @@ use core::ptr::{self, NonNull}; #[cfg(feature = "std")] use core::ops::{Deref, DerefMut}; -impl Report -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::, - object_ref: object_ref::, + object_drop: object_drop::, + object_ref: object_ref::, #[cfg(feature = "std")] - object_mut: object_mut::, - object_boxed: object_boxed::, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, + object_mut: object_mut::, + object_boxed: object_boxed::, + object_downcast: object_downcast::, + object_drop_rest: object_drop_front::, }; // 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 = MessageError(message); let vtable = &ErrorVTable { - object_drop: object_drop::, H>, - object_ref: object_ref::, H>, + object_drop: object_drop::>, + object_ref: object_ref::>, #[cfg(feature = "std")] - object_mut: object_mut::, H>, - object_boxed: object_boxed::, H>, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, + object_mut: object_mut::>, + object_boxed: object_boxed::>, + object_downcast: object_downcast::, + object_drop_rest: object_drop_front::, }; // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError 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 = DisplayError(message); let vtable = &ErrorVTable { - object_drop: object_drop::, H>, - object_ref: object_ref::, H>, + object_drop: object_drop::>, + object_ref: object_ref::>, #[cfg(feature = "std")] - object_mut: object_mut::, H>, - object_boxed: object_boxed::, H>, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, + object_mut: object_mut::>, + object_boxed: object_boxed::>, + object_downcast: object_downcast::, + object_drop_rest: object_drop_front::, }; // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError 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 = ContextError { msg, error }; let vtable = &ErrorVTable { - object_drop: object_drop::, H>, - object_ref: object_ref::, H>, + object_drop: object_drop::>, + object_ref: object_ref::>, #[cfg(feature = "std")] - object_mut: object_mut::, H>, - object_boxed: object_boxed::, H>, - object_downcast: context_downcast::, - object_drop_rest: context_drop_rest::, + object_mut: object_mut::>, + object_boxed: object_boxed::>, + object_downcast: context_downcast::, + object_drop_rest: context_drop_rest::, }; // 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) -> 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::, - object_ref: object_ref::, + object_drop: object_drop::, + object_ref: object_ref::, #[cfg(feature = "std")] - object_mut: object_mut::, - object_boxed: object_boxed::, - object_downcast: object_downcast::, H>, - object_drop_rest: object_drop_front::, H>, + object_mut: object_mut::, + object_boxed: object_boxed::, + object_downcast: object_downcast::>, + object_drop_rest: object_drop_front::>, }; // 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(error: E, vtable: &'static ErrorVTable, handler: Option) -> Self + unsafe fn construct( + error: E, + vtable: &'static ErrorVTable, + handler: Option>, + ) -> 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 is preserved in the vtable provided by the // caller rather than a builtin fat pointer vtable. - let erased = mem::transmute::>, Box>>(inner); + let erased = mem::transmute::>, Box>>(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> = ContextError { msg, error: self }; + let error: ContextError = ContextError { msg, error: self }; let vtable = &ErrorVTable { - object_drop: object_drop::>, H>, - object_ref: object_ref::>, H>, + object_drop: object_drop::>, + object_ref: object_ref::>, #[cfg(feature = "std")] - object_mut: object_mut::>, H>, - object_boxed: object_boxed::>, H>, - object_downcast: context_chain_downcast::, - object_drop_rest: context_chain_drop_rest::, + object_mut: object_mut::>, + object_boxed: object_boxed::>, + object_downcast: context_chain_downcast::, + object_drop_rest: context_chain_drop_rest::, }; // 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 From for Report +impl From for Report where - H: EyreHandler, E: StdError + Send + Sync + 'static, { fn from(error: E) -> Self { @@ -466,7 +471,7 @@ where } #[cfg(feature = "std")] -impl Deref for Report { +impl Deref for Report { type Target = dyn StdError + Send + Sync + 'static; fn deref(&self) -> &Self::Target { @@ -475,28 +480,25 @@ impl Deref for Report { } #[cfg(feature = "std")] -impl DerefMut for Report { +impl DerefMut for Report { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.error_mut() } } -impl Display for Report { +impl Display for Report { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.display(formatter) } } -impl Debug for Report { +impl Debug for Report { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.debug(formatter) } } -impl Drop for Report -where - H: EyreHandler, -{ +impl Drop for Report { fn drop(&mut self) { unsafe { // Read Box> from self. @@ -509,86 +511,72 @@ where } } -struct ErrorVTable -where - H: EyreHandler, -{ - object_drop: unsafe fn(Box>), - object_ref: unsafe fn(&ErrorImpl<(), H>) -> &(dyn StdError + Send + Sync + 'static), +struct ErrorVTable { + object_drop: unsafe fn(Box>), + 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>) -> Box, - object_downcast: unsafe fn(&ErrorImpl<(), H>, TypeId) -> Option>, - object_drop_rest: unsafe fn(Box>, TypeId), + object_boxed: unsafe fn(Box>) -> Box, + object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option>, + object_drop_rest: unsafe fn(Box>, TypeId), } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop(e: Box>) -where - H: EyreHandler, -{ +unsafe fn object_drop(e: Box>) { // Cast back to ErrorImpl so that the allocator receives the correct // Layout to deallocate the Box's memory. - let unerased = mem::transmute::>, Box>>(e); + let unerased = mem::transmute::>, Box>>(e); drop(unerased); } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop_front(e: Box>, target: TypeId) -where - H: EyreHandler, -{ +unsafe fn object_drop_front(e: Box>, 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, H>>>(e); + let unerased = mem::transmute::>, Box>>>(e); drop(unerased); } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_ref(e: &ErrorImpl<(), H>) -> &(dyn StdError + Send + Sync + 'static) +unsafe fn object_ref(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))._object + &(*(e as *const ErrorImpl<()> as *const ErrorImpl))._object } // Safety: requires layout of *e to match ErrorImpl. #[cfg(feature = "std")] -unsafe fn object_mut(e: &mut ErrorImpl<(), H>) -> &mut (dyn StdError + Send + Sync + 'static) +unsafe fn object_mut(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))._object + &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl))._object } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_boxed(e: Box>) -> Box +unsafe fn object_boxed(e: Box>) -> Box where - H: EyreHandler, E: StdError + Send + Sync + 'static, - H: Send + Sync + 'static, { // Attach ErrorImpl's native StdError vtable. The StdError impl is below. - mem::transmute::>, Box>>(e) + mem::transmute::>, Box>>(e) } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_downcast(e: &ErrorImpl<(), H>, target: TypeId) -> Option> +unsafe fn object_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> where - H: EyreHandler, E: 'static, { if TypeId::of::() == target { // Caller is looking for an E pointer and e is ErrorImpl, take a // pointer to its E field. - let unerased = e as *const ErrorImpl<(), H> as *const ErrorImpl; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl; 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>. #[cfg(feature = "std")] -unsafe fn context_downcast(e: &ErrorImpl<(), H>, target: TypeId) -> Option> +unsafe fn context_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> where - H: EyreHandler, D: 'static, E: 'static, { if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<(), H> as *const ErrorImpl, H>; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; let addr = &(*unerased)._object.msg as *const D as *mut (); Some(NonNull::new_unchecked(addr)) } else if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<(), H> as *const ErrorImpl, H>; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; 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>. #[cfg(feature = "std")] -unsafe fn context_drop_rest(e: Box>, target: TypeId) +unsafe fn context_drop_rest(e: Box>, 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::() == target { let unerased = mem::transmute::< - Box>, - Box, E>, H>>, + Box>, + Box, E>>>, >(e); drop(unerased); } else { let unerased = mem::transmute::< - Box>, - Box>, H>>, + Box>, + Box>>>, >(e); drop(unerased); } } // Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_chain_downcast(e: &ErrorImpl<(), H>, target: TypeId) -> Option> +unsafe fn context_chain_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> where - H: EyreHandler, D: 'static, { if TypeId::of::() == target { - let unerased = - e as *const ErrorImpl<(), H> as *const ErrorImpl>, H>; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; 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>, H>; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; let source = &(*unerased)._object.error; (source.inner.vtable.object_downcast)(&source.inner, target) } } // Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_chain_drop_rest(e: Box>, target: TypeId) +unsafe fn context_chain_drop_rest(e: Box>, 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::() == target { let unerased = mem::transmute::< - Box>, - Box, Report>, H>>, + Box>, + Box, Report>>>, >(e); // Drop the entire rest of the data structure rooted in the next Report. drop(unerased); } else { let unerased = mem::transmute::< - Box>, - Box>>, H>>, + Box>, + Box>>>, >(e); // Read out a ManuallyDrop>> 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 -where - H: EyreHandler, -{ - vtable: &'static ErrorVTable, - pub(crate) handler: Option, +pub(crate) struct ErrorImpl { + vtable: &'static ErrorVTable, + pub(crate) handler: Option>, // 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 { pub(crate) error: E, } -impl ErrorImpl -where - H: EyreHandler, -{ - fn erase(&self) -> &ErrorImpl<(), H> { +impl ErrorImpl { + 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 as *const ErrorImpl<(), H>) } + unsafe { &*(self as *const ErrorImpl as *const ErrorImpl<()>) } } } -impl 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 StdError for ErrorImpl +impl StdError for ErrorImpl where - H: EyreHandler, E: StdError, { fn source(&self) -> Option<&(dyn StdError + 'static)> { @@ -756,9 +728,8 @@ where } } -impl Debug for ErrorImpl +impl Debug for ErrorImpl where - H: EyreHandler, E: Debug, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -766,9 +737,8 @@ where } } -impl Display for ErrorImpl +impl Display for ErrorImpl where - H: EyreHandler, E: Display, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -776,8 +746,8 @@ where } } -impl From> for Box { - fn from(error: Report) -> Self { +impl From for Box { + fn from(error: Report) -> Self { let outer = ManuallyDrop::new(error); unsafe { // Read Box> from error. Can't move it out because @@ -792,21 +762,21 @@ impl From> for Box From> for Box { - fn from(error: Report) -> Self { +impl From for Box { + fn from(error: Report) -> Self { Box::::from(error) } } #[cfg(feature = "std")] -impl AsRef for Report { +impl AsRef for Report { fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { &**self } } #[cfg(feature = "std")] -impl AsRef for Report { +impl AsRef for Report { fn as_ref(&self) -> &(dyn StdError + 'static) { &**self } diff --git a/src/fmt.rs b/src/fmt.rs index a656c78..b4e97a3 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,11 +1,7 @@ use crate::error::ErrorImpl; -use crate::EyreHandler; use core::fmt; -impl ErrorImpl<(), H> -where - H: EyreHandler, -{ +impl ErrorImpl<()> { pub(crate) fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.handler .as_ref() diff --git a/src/kind.rs b/src/kind.rs index 1041236..393181b 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -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 AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} impl Adhoc { - pub fn new(self, message: M) -> Report + pub fn new(self, message: M) -> Report where M: Display + Debug + Send + Sync + 'static, { @@ -71,27 +71,21 @@ impl Adhoc { } } -pub struct Trait(std::marker::PhantomData); +pub struct Trait; -pub trait TraitKind: Sized { +pub trait TraitKind: Sized { #[inline] - fn eyre_kind(&self) -> Trait { - Trait(std::marker::PhantomData) + fn eyre_kind(&self) -> Trait { + Trait } } -impl TraitKind for E -where - E: Into>, - H: EyreHandler, -{ -} +impl TraitKind for E where E: Into {} -impl Trait { - pub fn new(self, error: E) -> Report +impl Trait { + pub fn new(self, error: E) -> Report where - E: Into>, - H: EyreHandler, + E: Into, { error.into() } @@ -113,7 +107,7 @@ impl BoxedKind for Box {} #[cfg(feature = "std")] impl Boxed { - pub fn new(self, error: Box) -> Report { + pub fn new(self, error: Box) -> 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::() } - fn _throw_error() -> Result<(), Report> { + fn _throw_error() -> Result<(), Report> { match _parse("abc") { Ok(_) => Ok(()), Err(e) => Err(eyre!(e).wrap_err("try parsing an actual number")), diff --git a/src/lib.rs b/src/lib.rs index ca053bf..c90731b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 -where - H: EyreHandler, -{ - inner: ManuallyDrop>>, +pub struct Report { + inner: ManuallyDrop>>, +} + +type ErrorHook = + Box Box + Sync + Send + 'static>; + +static HOOK: OnceCell = OnceCell::new(); + +/// +pub fn set_hook(hook: ErrorHook) -> Result<(), Box> { + HOOK.set(hook) + .map_err(|_| "unable to set global hook".into()) +} + +fn capture_handler(error: &(dyn StdError + 'static)) -> Box { + HOOK.get_or_init(|| Box::new(DefaultHandler::default_with))(error) +} + +impl dyn EyreHandler { + /// + pub fn is(&self) -> bool { + // Get `TypeId` of the type this function is instantiated with. + let t = core::any::TypeId::of::(); + + // 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(&self) -> Option<&T> { + if self.is::() { + unsafe { Some(&*(self as *const dyn EyreHandler as *const T)) } + } else { + None + } + } + + /// + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.is::() { + 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; /// type Result> = core::result::Result; /// ``` -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, } +impl DefaultHandler { + #[allow(unused_variables)] + fn default_with(error: &(dyn StdError + 'static)) -> Box { + 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> = core::result::Result; +pub type Result = core::result::Result; /// Provides the `wrap_err` method for `Result`. /// @@ -1022,29 +1012,26 @@ pub type Result> = core::result::Result; /// # panic!("expected downcast to succeed"); /// } /// ``` -pub trait WrapErr: context::private::Sealed -where - H: EyreHandler, -{ +pub trait WrapErr: context::private::Sealed { /// Wrap the error value with a new adhoc error - fn wrap_err(self, msg: D) -> Result> + fn wrap_err(self, msg: D) -> Result 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(self, f: F) -> Result> + fn wrap_err_with(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; /// Compatibility re-export of wrap_err for interopt with `anyhow` - fn context(self, msg: D) -> Result> + fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Compatibility re-export of wrap_err_with for interopt with `anyhow` - fn with_context(self, f: F) -> Result> + fn with_context(self, f: F) -> Result 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: context::private::Sealed -where - H: EyreHandler, -{ +pub trait ContextCompat: context::private::Sealed { /// Compatibility version of `wrap_err` for creating new errors with new source on `Option` /// when porting from `anyhow` - fn context(self, msg: D) -> Result> + fn context(self, msg: D) -> Result 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(self, f: F) -> Result> + fn with_context(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; /// Compatibility re-export of `context` for porting from `anyhow` to `eyre` - fn wrap_err(self, msg: D) -> Result> + fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Compatibility re-export of `with_context` for porting from `anyhow` to `eyre` - fn wrap_err_with(self, f: F) -> Result> + fn wrap_err_with(self, f: F) -> Result 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(message: M) -> Report + pub fn new_adhoc(message: M) -> Report where - H: EyreHandler, M: Display + Debug + Send + Sync + 'static, { Report::from_adhoc(message) diff --git a/tests/test_downcast.rs b/tests/test_downcast.rs index a098f8a..f025907 100644 --- a/tests/test_downcast.rs +++ b/tests/test_downcast.rs @@ -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 = Report::new(LargeAlignedError("oh no!")); + let error = Report::new(LargeAlignedError("oh no!")); assert_eq!( "oh no!", error.downcast_ref::().unwrap().0 diff --git a/tests/test_repr.rs b/tests/test_repr.rs index 3f70d39..9e595bd 100644 --- a/tests/test_repr.rs +++ b/tests/test_repr.rs @@ -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::::new(DetectDrop::new(&has_dropped))); + drop(Report::new(DetectDrop::new(&has_dropped))); assert!(has_dropped.get()); } diff --git a/tests/test_source.rs b/tests/test_source.rs index 6d976a0..d7079f2 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -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();