From 125142eb74e4aab1343c2f0f81d46b3a197ef218 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 6 Jun 2020 11:54:19 -0700 Subject: [PATCH 01/10] Proof of concept of dyn dispatch based report handler --- Cargo.toml | 3 +++ src/error.rs | 9 +++++++++ src/fmt.rs | 15 +++++++++++++-- src/lib.rs | 24 ++++++++++++++++++++++++ tests/test_macros.rs | 1 + 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee99ab7..4470692 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ rustversion = "1.0" thiserror = "1.0" trybuild = { version = "1.0.19", features = ["diff"] } +[dependencies] +once_cell = "1.4.0" + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/src/error.rs b/src/error.rs index 80d879f..99be3f4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -190,9 +190,13 @@ impl Error { where E: StdError + Send + Sync + 'static, { + let handler = + crate::HOOK.get_or_init(|| Box::new(|_| Box::new(crate::DefaultHandler)))(&error); + let inner = Box::new(ErrorImpl { vtable, backtrace, + handler, _object: error, }); // Erase the concrete type of E from the compile-time type system. This @@ -681,6 +685,7 @@ where pub(crate) struct ErrorImpl { vtable: &'static ErrorVTable, backtrace: Option, + handler: Box, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, @@ -728,6 +733,10 @@ impl ErrorImpl<()> { .expect("backtrace capture failed") } + pub(crate) fn handler(&self) -> &dyn crate::ReportHandler { + self.handler.as_ref() + } + pub(crate) fn chain(&self) -> Chain { Chain::new(self.error()) } diff --git a/src/fmt.rs b/src/fmt.rs index be93e1a..b72dba2 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,6 +1,6 @@ use crate::chain::Chain; use crate::error::ErrorImpl; -use core::fmt::{self, Debug, Write}; +use core::fmt::{self, Write}; impl ErrorImpl<()> { pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -17,9 +17,20 @@ impl ErrorImpl<()> { pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { let error = self.error(); + let handler = self.handler(); + handler.report(error, f) + } +} + +impl crate::ReportHandler for crate::DefaultHandler { + fn report( + &self, + error: &(dyn crate::StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { if f.alternate() { - return Debug::fmt(error, f); + return core::fmt::Debug::fmt(error, f); } write!(f, "{}", error)?; diff --git a/src/lib.rs b/src/lib.rs index 684c1ab..8f6a7b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,6 +247,7 @@ trait StdError: Debug + Display { } pub use anyhow as format_err; +use once_cell::sync::OnceCell; /// The `Error` type, a wrapper around a dynamic error type. /// @@ -584,6 +585,29 @@ pub trait Context: context::private::Sealed { F: FnOnce() -> C; } +static HOOK: OnceCell = OnceCell::new(); + +pub trait ReportHandler: core::any::Any { + fn report( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result; +} + +type ErrorHook = Box< + dyn Fn(&(dyn StdError + 'static)) -> Box + + Sync + + Send + + 'static, +>; + +pub fn set_hook(hook: ErrorHook) -> Result<(), ErrorHook> { + HOOK.set(hook) +} + +struct DefaultHandler; + // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { diff --git a/tests/test_macros.rs b/tests/test_macros.rs index c6888b6..b9339b0 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -11,6 +11,7 @@ fn test_messages() { } #[test] +#[allow(clippy::eq_op)] fn test_ensure() { let f = || { ensure!(1 + 1 == 2, "This is correct"); From bdb37be10d5f060442f5d4315f0b035cebd8cf96 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 6 Jun 2020 12:12:23 -0700 Subject: [PATCH 02/10] try to setup proof of concept of downcast stuff --- src/error.rs | 12 ++++++++++++ src/lib.rs | 3 ++- tests/test_handler.rs | 44 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/test_handler.rs diff --git a/src/error.rs b/src/error.rs index 99be3f4..b323c1f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -314,6 +314,14 @@ impl Error { self.inner.backtrace() } + pub fn handler(&self) -> &dyn crate::ReportHandler { + self.inner.handler() + } + + pub fn handler_mut(&mut self) -> &mut dyn crate::ReportHandler { + self.inner.handler_mut() + } + /// An iterator of the chain of source errors contained by this Error. /// /// This iterator will visit every error in the cause chain of this error @@ -737,6 +745,10 @@ impl ErrorImpl<()> { self.handler.as_ref() } + pub(crate) fn handler_mut(&mut self) -> &mut dyn crate::ReportHandler { + self.handler.as_mut() + } + pub(crate) fn chain(&self) -> Chain { Chain::new(self.error()) } diff --git a/src/lib.rs b/src/lib.rs index 8f6a7b6..5772d51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -602,8 +602,9 @@ type ErrorHook = Box< + 'static, >; -pub fn set_hook(hook: ErrorHook) -> Result<(), ErrorHook> { +pub fn set_hook(hook: ErrorHook) -> Result<(), Box> { HOOK.set(hook) + .map_err(|_| "unable to set global hook".into()) } struct DefaultHandler; diff --git a/tests/test_handler.rs b/tests/test_handler.rs new file mode 100644 index 0000000..e9ce41c --- /dev/null +++ b/tests/test_handler.rs @@ -0,0 +1,44 @@ +struct CustomHandler { + msg: &'static str, +} + +impl anyhow::ReportHandler for CustomHandler { + fn report( + &self, + _error: &(dyn std::error::Error + 'static), + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + write!(f, "{}", self.msg) + } +} + +#[test] +fn test_custom_hook() { + let expected = "hook is set!"; + anyhow::set_hook(Box::new(move |_error| { + Box::new(CustomHandler { msg: expected }) + })) + .unwrap(); + + let report = anyhow::anyhow!("heres the message!"); + let actual = format!("{:?}", report); + + assert_eq!(expected, actual); +} + +#[test] +fn test_mutable_hook() { + let fake_expected = "hook is set!"; + let real_expected = "the context was modified!"; + + anyhow::set_hook(Box::new(move |_error| { + Box::new(CustomHandler { msg: fake_expected }) + })) + .unwrap(); + + let report = anyhow::anyhow!("heres the message!"); + let handler: &mut dyn core::any::Any = report.handler_mut() as _; + let actual = format!("{:?}", report); + + assert_eq!(real_expected, actual); +} From 9d734ffa94c2ceb774616baed590eb3d2b49640f Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 6 Jun 2020 12:17:54 -0700 Subject: [PATCH 03/10] insert real syntax that was expected to work --- tests/test_handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_handler.rs b/tests/test_handler.rs index e9ce41c..6b03a6f 100644 --- a/tests/test_handler.rs +++ b/tests/test_handler.rs @@ -36,8 +36,8 @@ fn test_mutable_hook() { })) .unwrap(); - let report = anyhow::anyhow!("heres the message!"); - let handler: &mut dyn core::any::Any = report.handler_mut() as _; + let mut report = anyhow::anyhow!("heres the message!"); + report.handler_mut().downcast_mut::().msg = real_expected; let actual = format!("{:?}", report); assert_eq!(real_expected, actual); From 24ee496bcd79cc175f73e1f5757da5225ed924c7 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 6 Jun 2020 12:25:31 -0700 Subject: [PATCH 04/10] fix downcasting --- src/lib.rs | 29 +++++++++++++++++++++++++++++ tests/test_handler.rs | 26 ++++++++++++++------------ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5772d51..5c6d0a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -607,6 +607,35 @@ pub fn set_hook(hook: ErrorHook) -> Result<(), Box(&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 ReportHandler as *const T)) } + } else { + None + } + } + + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.is::() { + unsafe { Some(&mut *(self as *mut dyn ReportHandler as *mut T)) } + } else { + None + } + } +} + struct DefaultHandler; // Not public API. Referenced by macro-generated code. diff --git a/tests/test_handler.rs b/tests/test_handler.rs index 6b03a6f..de7ac14 100644 --- a/tests/test_handler.rs +++ b/tests/test_handler.rs @@ -12,32 +12,34 @@ impl anyhow::ReportHandler for CustomHandler { } } +static EXPECTED: &str = "hook is set!"; + #[test] fn test_custom_hook() { - let expected = "hook is set!"; - anyhow::set_hook(Box::new(move |_error| { - Box::new(CustomHandler { msg: expected }) - })) - .unwrap(); + let _ = anyhow::set_hook(Box::new(move |_error| { + Box::new(CustomHandler { msg: EXPECTED }) + })); let report = anyhow::anyhow!("heres the message!"); let actual = format!("{:?}", report); - assert_eq!(expected, actual); + assert_eq!(EXPECTED, actual); } #[test] fn test_mutable_hook() { - let fake_expected = "hook is set!"; let real_expected = "the context was modified!"; - anyhow::set_hook(Box::new(move |_error| { - Box::new(CustomHandler { msg: fake_expected }) - })) - .unwrap(); + let _ = anyhow::set_hook(Box::new(move |_error| { + Box::new(CustomHandler { msg: EXPECTED }) + })); let mut report = anyhow::anyhow!("heres the message!"); - report.handler_mut().downcast_mut::().msg = real_expected; + report + .handler_mut() + .downcast_mut::() + .unwrap() + .msg = real_expected; let actual = format!("{:?}", report); assert_eq!(real_expected, actual); From 89cb798be63a20e26f313ecf3cce67f4b20d6cc0 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 6 Jun 2020 12:28:24 -0700 Subject: [PATCH 05/10] add comments --- tests/test_handler.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_handler.rs b/tests/test_handler.rs index de7ac14..1f32076 100644 --- a/tests/test_handler.rs +++ b/tests/test_handler.rs @@ -16,6 +16,8 @@ static EXPECTED: &str = "hook is set!"; #[test] fn test_custom_hook() { + // discard the result because the tests in the same file race against + // eachother to set the global hook and one will panic let _ = anyhow::set_hook(Box::new(move |_error| { Box::new(CustomHandler { msg: EXPECTED }) })); @@ -30,6 +32,8 @@ fn test_custom_hook() { fn test_mutable_hook() { let real_expected = "the context was modified!"; + // discard the result because the tests in the same file race against + // eachother to set the global hook and one will panic let _ = anyhow::set_hook(Box::new(move |_error| { Box::new(CustomHandler { msg: EXPECTED }) })); From 5b36a0d6826196336b6de50c1a83613e2802096b Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 18 Jul 2020 13:03:32 -0700 Subject: [PATCH 06/10] refactor backtrace logic --- src/backtrace.rs | 27 ------------------- src/context.rs | 7 +++-- src/error.rs | 60 +++++++++++++++++-------------------------- src/fmt.rs | 17 +++++++++--- src/kind.rs | 8 ++---- src/lib.rs | 51 +++++++++++++++++++++++++++++++----- src/wrapper.rs | 2 +- tests/test_handler.rs | 12 ++++++++- 8 files changed, 98 insertions(+), 86 deletions(-) diff --git a/src/backtrace.rs b/src/backtrace.rs index 01e33cb..ad2eaaf 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -1,23 +1,3 @@ -#[cfg(backtrace)] -pub(crate) use std::backtrace::Backtrace; - -#[cfg(not(backtrace))] -pub(crate) enum Backtrace {} - -#[cfg(backtrace)] -macro_rules! backtrace { - () => { - Some(Backtrace::capture()) - }; -} - -#[cfg(not(backtrace))] -macro_rules! backtrace { - () => { - None - }; -} - #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { @@ -27,10 +7,3 @@ macro_rules! backtrace_if_absent { } }; } - -#[cfg(all(feature = "std", not(backtrace)))] -macro_rules! backtrace_if_absent { - ($err:expr) => { - None - }; -} diff --git a/src/context.rs b/src/context.rs index 25d3411..cf85f8a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -24,8 +24,7 @@ mod ext { where C: Display + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(self); - Error::from_context(context, self, backtrace) + Error::from_context(context, self) } } @@ -84,7 +83,7 @@ impl Context for Option { where C: Display + Send + Sync + 'static, { - self.ok_or_else(|| Error::from_display(context, backtrace!())) + self.ok_or_else(|| Error::from_display(context)) } fn with_context(self, context: F) -> Result @@ -92,7 +91,7 @@ impl Context for Option { C: Display + Send + Sync + 'static, F: FnOnce() -> C, { - self.ok_or_else(|| Error::from_display(context(), backtrace!())) + self.ok_or_else(|| Error::from_display(context())) } } diff --git a/src/error.rs b/src/error.rs index b323c1f..82a9de3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,12 @@ use crate::alloc::Box; -use crate::backtrace::Backtrace; use crate::chain::Chain; use crate::{Error, StdError}; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::{self, ManuallyDrop}; use core::ptr::{self, NonNull}; +#[cfg(backtrace)] +use std::backtrace::Backtrace; #[cfg(feature = "std")] use core::ops::{Deref, DerefMut}; @@ -24,8 +25,7 @@ impl Error { where E: StdError + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(error); - Error::from_std(error, backtrace) + Error::from_std(error) } /// Create a new error object from a printable error message. @@ -69,11 +69,11 @@ impl Error { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message) } #[cfg(feature = "std")] - pub(crate) fn from_std(error: E, backtrace: Option) -> Self + pub(crate) fn from_std(error: E) -> Self where E: StdError + Send + Sync + 'static, { @@ -88,10 +88,10 @@ impl Error { }; // Safety: passing vtable that operates on the right type E. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable) } } - pub(crate) fn from_adhoc(message: M, backtrace: Option) -> Self + pub(crate) fn from_adhoc(message: M) -> Self where M: Display + Debug + Send + Sync + 'static, { @@ -109,10 +109,10 @@ impl Error { // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable) } } - pub(crate) fn from_display(message: M, backtrace: Option) -> Self + pub(crate) fn from_display(message: M) -> Self where M: Display + Send + Sync + 'static, { @@ -130,11 +130,11 @@ impl Error { // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable) } } #[cfg(feature = "std")] - pub(crate) fn from_context(context: C, error: E, backtrace: Option) -> Self + pub(crate) fn from_context(context: C, error: E) -> Self where C: Display + Send + Sync + 'static, E: StdError + Send + Sync + 'static, @@ -152,14 +152,11 @@ impl Error { }; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable) } } #[cfg(feature = "std")] - pub(crate) fn from_boxed( - error: Box, - backtrace: Option, - ) -> Self { + pub(crate) fn from_boxed(error: Box) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); let vtable = &ErrorVTable { @@ -174,7 +171,7 @@ impl Error { // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable) } } // Takes backtrace as argument rather than capturing it here so that the @@ -182,20 +179,17 @@ impl Error { // // Unsafe because the given vtable must have sensible behavior on the error // value of type E. - unsafe fn construct( - error: E, - vtable: &'static ErrorVTable, - backtrace: Option, - ) -> Self + unsafe fn construct(error: E, vtable: &'static ErrorVTable) -> Self where E: StdError + Send + Sync + 'static, { - let handler = - crate::HOOK.get_or_init(|| Box::new(|_| Box::new(crate::DefaultHandler)))(&error); + let handler = crate::HOOK + .get_or_init(|| Box::new(|error| Box::new(crate::DefaultHandler::new(error))))( + &error + ); let inner = Box::new(ErrorImpl { vtable, - backtrace, handler, _object: error, }); @@ -283,11 +277,8 @@ impl Error { object_drop_rest: context_chain_drop_rest::, }; - // As the cause is anyhow::Error, we already have a backtrace for it. - let backtrace = None; - // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable) } } /// Get the backtrace for this Error. @@ -481,8 +472,7 @@ where E: StdError + Send + Sync + 'static, { fn from(error: E) -> Self { - let backtrace = backtrace_if_absent!(error); - Error::from_std(error, backtrace) + Error::from_std(error) } } @@ -692,8 +682,7 @@ where #[repr(C)] pub(crate) struct ErrorImpl { vtable: &'static ErrorVTable, - backtrace: Option, - handler: Box, + handler: Box, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, @@ -735,10 +724,7 @@ impl ErrorImpl<()> { // This unwrap can only panic if the underlying error's backtrace method // is nondeterministic, which would only happen in maliciously // constructed code. - self.backtrace - .as_ref() - .or_else(|| self.error().backtrace()) - .expect("backtrace capture failed") + self.handler.backtrace(self.error()) } pub(crate) fn handler(&self) -> &dyn crate::ReportHandler { diff --git a/src/fmt.rs b/src/fmt.rs index b72dba2..617c0ca 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -19,12 +19,23 @@ impl ErrorImpl<()> { let error = self.error(); let handler = self.handler(); - handler.report(error, f) + handler.debug(error, f) } } impl crate::ReportHandler for crate::DefaultHandler { - fn report( + #[cfg(backtrace)] + fn backtrace<'a>( + &'a self, + error: &'a (dyn crate::StdError + 'static), + ) -> &'a std::backtrace::Backtrace { + error + .backtrace() + .or_else(|| self.backtrace.as_ref()) + .expect("backtrace must have been captured") + } + + fn debug( &self, error: &(dyn crate::StdError + 'static), f: &mut core::fmt::Formatter<'_>, @@ -53,7 +64,7 @@ impl crate::ReportHandler for crate::DefaultHandler { { use std::backtrace::BacktraceStatus; - let backtrace = self.backtrace(); + let backtrace = self.backtrace(error); if let BacktraceStatus::Captured = backtrace.status() { let mut backtrace = backtrace.to_string(); write!(f, "\n\n")?; diff --git a/src/kind.rs b/src/kind.rs index fdeb060..9574b06 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -50,9 +50,6 @@ use core::fmt::{Debug, Display}; #[cfg(feature = "std")] use crate::StdError; -#[cfg(backtrace)] -use std::backtrace::Backtrace; - pub struct Adhoc; pub trait AdhocKind: Sized { @@ -69,7 +66,7 @@ impl Adhoc { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message) } } @@ -110,7 +107,6 @@ impl BoxedKind for Box {} #[cfg(feature = "std")] impl Boxed { pub fn new(self, error: Box) -> Error { - let backtrace = backtrace_if_absent!(error); - Error::from_boxed(error, backtrace) + Error::from_boxed(error) } } diff --git a/src/lib.rs b/src/lib.rs index 5c6d0a0..24a9be5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -239,6 +239,9 @@ use core::fmt::Debug; #[cfg(feature = "std")] use std::error::Error as StdError; +#[cfg(backtrace)] +use std::backtrace::Backtrace; + #[cfg(not(feature = "std"))] trait StdError: Debug + Display { fn source(&self) -> Option<&(dyn StdError + 'static)> { @@ -587,12 +590,32 @@ pub trait Context: context::private::Sealed { static HOOK: OnceCell = OnceCell::new(); -pub trait ReportHandler: core::any::Any { - fn report( +pub trait ReportHandler: core::any::Any + Send + Sync { + fn debug( &self, error: &(dyn StdError + 'static), f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result; + + #[cfg(backtrace)] + fn backtrace<'a>(&'a self, error: &'a (dyn StdError + 'static)) -> &'a Backtrace; + + /// Override for the `Display` format + fn display( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + write!(f, "{}", error)?; + + if f.alternate() { + for cause in crate::chain::Chain::new(error).skip(1) { + write!(f, ": {}", cause)?; + } + } + + Ok(()) + } } type ErrorHook = Box< @@ -636,7 +659,24 @@ impl dyn ReportHandler { } } -struct DefaultHandler; +struct DefaultHandler { + #[cfg(backtrace)] + backtrace: Option, +} + +impl DefaultHandler { + #[cfg(backtrace)] + fn new(error: &(dyn StdError + 'static)) -> Self { + let backtrace = backtrace_if_absent!(error); + + Self { backtrace } + } + + #[cfg(not(backtrace))] + fn new(_error: &(dyn StdError + 'static)) -> Self { + Self {} + } +} // Not public API. Referenced by macro-generated code. #[doc(hidden)] @@ -644,9 +684,6 @@ pub mod private { use crate::Error; use core::fmt::{Debug, Display}; - #[cfg(backtrace)] - use std::backtrace::Backtrace; - pub use core::result::Result::Err; #[doc(hidden)] @@ -661,6 +698,6 @@ pub mod private { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message) } } diff --git a/src/wrapper.rs b/src/wrapper.rs index 3ebe51a..99c3482 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -68,7 +68,7 @@ impl Display for BoxedError { #[cfg(feature = "std")] impl StdError for BoxedError { #[cfg(backtrace)] - fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> { + fn backtrace(&self) -> Option<&std::backtrace::Backtrace> { self.0.backtrace() } diff --git a/tests/test_handler.rs b/tests/test_handler.rs index 1f32076..b59155a 100644 --- a/tests/test_handler.rs +++ b/tests/test_handler.rs @@ -1,9 +1,19 @@ +#![cfg_attr(backtrace, feature(backtrace))] + struct CustomHandler { msg: &'static str, } impl anyhow::ReportHandler for CustomHandler { - fn report( + #[cfg(backtrace)] + fn backtrace<'a>( + &'a self, + _error: &'a (dyn std::error::Error + 'static), + ) -> &std::backtrace::Backtrace { + unimplemented!() + } + + fn debug( &self, _error: &(dyn std::error::Error + 'static), f: &mut std::fmt::Formatter<'_>, From e7919fbdd8e68f420d22ec26ddfee92abbded98f Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 18 Jul 2020 13:17:13 -0700 Subject: [PATCH 07/10] hmmmmmm --- Cargo.toml | 2 +- src/error.rs | 3 +++ src/fmt.rs | 2 +- src/lib.rs | 21 +++++++++++++++++---- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4470692..cf7b134 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ readme = "README.md" categories = ["rust-patterns"] [features] -default = ["std"] +default = [] std = [] [dev-dependencies] diff --git a/src/error.rs b/src/error.rs index 82a9de3..f778976 100644 --- a/src/error.rs +++ b/src/error.rs @@ -305,10 +305,12 @@ impl Error { self.inner.backtrace() } + #[cfg(feature = "std")] pub fn handler(&self) -> &dyn crate::ReportHandler { self.inner.handler() } + #[cfg(feature = "std")] pub fn handler_mut(&mut self) -> &mut dyn crate::ReportHandler { self.inner.handler_mut() } @@ -731,6 +733,7 @@ impl ErrorImpl<()> { self.handler.as_ref() } + #[cfg(feature = "std")] pub(crate) fn handler_mut(&mut self) -> &mut dyn crate::ReportHandler { self.handler.as_mut() } diff --git a/src/fmt.rs b/src/fmt.rs index 617c0ca..e38f69f 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -119,7 +119,7 @@ where } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index 24a9be5..6c49163 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,6 +242,9 @@ use std::error::Error as StdError; #[cfg(backtrace)] use std::backtrace::Backtrace; +#[cfg(feature = "std")] +pub use {set_hook, ReportHandler}; + #[cfg(not(feature = "std"))] trait StdError: Debug + Display { fn source(&self) -> Option<&(dyn StdError + 'static)> { @@ -590,7 +593,7 @@ pub trait Context: context::private::Sealed { static HOOK: OnceCell = OnceCell::new(); -pub trait ReportHandler: core::any::Any + Send + Sync { +trait ReportHandler: core::any::Any + Send + Sync { fn debug( &self, error: &(dyn StdError + 'static), @@ -625,11 +628,21 @@ type ErrorHook = Box< + 'static, >; -pub fn set_hook(hook: ErrorHook) -> Result<(), Box> { - HOOK.set(hook) - .map_err(|_| "unable to set global hook".into()) +#[derive(Debug)] +pub struct HookError; + +impl core::fmt::Display for HookError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("unable to set global hook") + } +} + +#[cfg(feature = "std")] +pub fn set_hook(hook: ErrorHook) -> Result<(), HookError> { + HOOK.set(hook).map_err(|_| HookError) } +#[cfg(feature = "std")] impl dyn ReportHandler { pub fn is(&self) -> bool { // Get `TypeId` of the type this function is instantiated with. From 612e25e0bb094d61bb4814202d9033791a0b2157 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 18 Jul 2020 13:19:19 -0700 Subject: [PATCH 08/10] back to defaults --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cf7b134..4470692 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ readme = "README.md" categories = ["rust-patterns"] [features] -default = [] +default = ["std"] std = [] [dev-dependencies] From 3489703e4573d51edd7d6bd3141d3d7b7df7d2e4 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 18 Jul 2020 13:26:49 -0700 Subject: [PATCH 09/10] a little closer --- src/fmt.rs | 11 +++-------- src/lib.rs | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index e38f69f..e2f6997 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -4,15 +4,10 @@ use core::fmt::{self, Write}; impl ErrorImpl<()> { pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.error())?; - - if f.alternate() { - for cause in self.chain().skip(1) { - write!(f, ": {}", cause)?; - } - } + let error = self.error(); + let handler = self.handler(); - Ok(()) + handler.display(error, f) } pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/lib.rs b/src/lib.rs index 6c49163..7210139 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,9 +242,6 @@ use std::error::Error as StdError; #[cfg(backtrace)] use std::backtrace::Backtrace; -#[cfg(feature = "std")] -pub use {set_hook, ReportHandler}; - #[cfg(not(feature = "std"))] trait StdError: Debug + Display { fn source(&self) -> Option<&(dyn StdError + 'static)> { @@ -593,6 +590,36 @@ pub trait Context: context::private::Sealed { static HOOK: OnceCell = OnceCell::new(); +#[cfg(feature = "std")] +pub trait ReportHandler: core::any::Any + Send + Sync { + fn debug( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result; + + #[cfg(backtrace)] + fn backtrace<'a>(&'a self, error: &'a (dyn StdError + 'static)) -> &'a Backtrace; + + /// Override for the `Display` format + fn display( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + write!(f, "{}", error)?; + + if f.alternate() { + for cause in crate::chain::Chain::new(error).skip(1) { + write!(f, ": {}", cause)?; + } + } + + Ok(()) + } +} + +#[cfg(not(feature = "std"))] trait ReportHandler: core::any::Any + Send + Sync { fn debug( &self, From e65b8737c9c641806496be7851d75c4be34557b6 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Sat, 18 Jul 2020 13:34:33 -0700 Subject: [PATCH 10/10] cleanup warnings --- src/error.rs | 1 + src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/error.rs b/src/error.rs index f778976..dac6d34 100644 --- a/src/error.rs +++ b/src/error.rs @@ -738,6 +738,7 @@ impl ErrorImpl<()> { self.handler.as_mut() } + #[cfg(feature = "std")] pub(crate) fn chain(&self) -> Chain { Chain::new(self.error()) } diff --git a/src/lib.rs b/src/lib.rs index 7210139..dbf4a5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -656,14 +656,19 @@ type ErrorHook = Box< >; #[derive(Debug)] +#[cfg(feature = "std")] pub struct HookError; +#[cfg(feature = "std")] impl core::fmt::Display for HookError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str("unable to set global hook") } } +#[cfg(feature = "std")] +impl StdError for HookError {} + #[cfg(feature = "std")] pub fn set_hook(hook: ErrorHook) -> Result<(), HookError> { HOOK.set(hook).map_err(|_| HookError)