Switch report handler to a global hook (#29)

* Switch report handler to a global hook

* more cleanup

* remove default from example

* remove broken support for no_std

* back out the unneeded nostd check

* remove one stackframe when capturing a Handler

* update docs and prep for release

* port lib.rs docs updates to readme
This commit is contained in:
Jane Lusby 2020-07-05 19:04:31 -07:00 committed by GitHub
parent 96ddb39cd0
commit f39eaff0b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 458 additions and 612 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "eyre"
version = "0.5.0" # remember to update html_root_url
version = "0.6.0" # remember to update html_root_url
authors = ["David Tolnay <dtolnay@gmail.com>", "Jane Lusby <jlusby42@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@ -10,10 +10,6 @@ documentation = "https://docs.rs/eyre"
readme = "README.md"
categories = ["rust-patterns"]
[features]
default = ["std"]
std = []
[dev-dependencies]
futures = { version = "0.3", default-features = false }
rustversion = "1.0"
@ -24,6 +20,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

@ -12,21 +12,16 @@ This library provides [`eyre::Report`][Report], a trait object based
error handling type for easy idiomatic error handling and reporting in Rust
applications.
This crate is a fork of [`anyhow`] by @dtolnay with a support for customized
`Reports`. For more details on customization checkout the docs on
This crate is a fork of [`anyhow`] with a support for customized
error reports. For more details on customization checkout the docs on
[`eyre::EyreHandler`].
```toml
[dependencies]
eyre = "0.5"
```
## Custom Report Handlers
The heart of this crate is its ability to swap out the Handler type to change
The heart of this crate is it's ability to swap out the Handler type to change
what information is carried alongside errors and how the end report is
formatted. This crate is meant to be used alongside companion crates that
customize its behavior. Below is a list of known crates that export report
customize it's behavior. Below is a list of known crates that export report
handlers for eyre and short summaries of what features they provide.
- [`stable-eyre`]: Switches the backtrace type from `std`'s to `backtrace-rs`'s
@ -36,11 +31,11 @@ handlers for eyre and short summaries of what features they provide.
`tracing_error::SpanTrace`. Provides a `Help` trait for attaching warnings
and suggestions to error reports. The end report is then pretty printed with
the help of [`color-backtrace`], [`color-spantrace`], and `ansi_term`. Check
out the README on [`color-eyre`] for screenshots of the report format.
out the README on [`color-eyre`] for details on the report format.
- [`simple-eyre`]: A minimal `EyreHandler` that captures no additional
information, for when you do not wish to capture `Backtrace`s with errors.
- [`jane-eyre`]: A a report handler crate that exists purely for the pun.
Currently just re-exports `color-eyre`.
- [`jane-eyre`]: A report handler crate that exists purely for the pun.
Currently just re-exports `color-eyre`.
## Details
@ -153,7 +148,7 @@ Cargo.toml. A global allocator is required.
```toml
[dependencies]
eyre = { version = "0.5", default-features = false }
eyre = { version = "0.6", default-features = false }
```
Since the `?`-based error conversions would normally rely on the
@ -172,10 +167,11 @@ the necessary improvements for this to be possible as part of [RFC 2504].
## Comparison to thiserror
Use Eyre if you don't care what error type your functions return, you just
want it to be easy. This is common in application code. Use [thiserror] if you
are a library that wants to design your own dedicated error type(s) so that on
failures the caller gets exactly the information that you choose.
Use `eyre` if you don't think you'll do anything with an error other than
report it. This is common in application code. Use `thiserror` if you think
you need an error type that can be handled via match or reported. This is
common in library crates where you don't know how your users will handle
your errors.
[thiserror]: https://github.com/dtolnay/thiserror
@ -183,40 +179,7 @@ failures the caller gets exactly the information that you choose.
This crate does its best to be usable as a drop in replacement of `anyhow` and
vice-versa by `re-exporting` all of the renamed APIs with the names used in
`anyhow`.
There are two main incompatibilities that you might encounter when porting a
codebase from `anyhow` to `eyre`:
- type inference errors when using `eyre!`
- `.context` not being implemented for `Option`
#### Type Inference Errors
The type inference issue is caused by the generic parameter, which isn't
present in `anyhow::Error`. Specifically, the following works in anyhow:
```rust
use anyhow::anyhow;
// Works
let val = get_optional_val().ok_or_else(|| anyhow!("failed to get value")).unwrap_err();
```
Where as with `eyre!` this will fail due to being unable to infer the type for
the Handler parameter. The solution to this problem, should you encounter it,
is to give the compiler a hint for what type it should be resolving to, either
via your return type or a type annotation.
```rust,compile_fail
use eyre::eyre;
// Broken
let val = get_optional_val().ok_or_else(|| eyre!("failed to get value")).unwrap();
// Works
let val: Report = get_optional_val().ok_or_else(|| eyre!("failed to get value")).unwrap();
```
`anyhow`, though there are some differences still.
#### `Context` and `Option`
@ -246,7 +209,7 @@ let opt: Option<()> = None;
let result: Result<()> = opt.ok_or_else(|| eyre!("new error message"));
```
However, to help with porting we do provide a `ContextCompat` trait which
**NOTE**: However, to help with porting we do provide a `ContextCompat` trait which
implements `context` for options which you can import to make existing
`.context` calls compile.

View File

@ -35,9 +35,6 @@ const PROBE: &str = r#"
"#;
fn main() {
if !cfg!(feature = "std") {
return;
}
match compile_probe() {
Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
_ => {}

View File

@ -0,0 +1,81 @@
use backtrace::Backtrace;
use eyre::EyreHandler;
use std::error::Error;
use std::{fmt, iter};
fn main() -> eyre::Result<()> {
// Install our custom eyre report hook for constructing our custom Handlers
install().unwrap();
// construct a report with, hopefully, our custom handler!
let mut report = eyre::eyre!("hello from custom error town!");
// manually set the custom msg for this report after it has been constructed
if let Some(handler) = report.handler_mut().downcast_mut::<Handler>() {
handler.custom_msg = Some("you're the best users, you know that right???");
}
// print that shit!!
Err(report)
}
// define a handler that captures backtraces unless told not to
fn install() -> Result<(), impl Error> {
let capture_backtrace = std::env::var("RUST_BACKWARDS_TRACE")
.map(|val| val != "0")
.unwrap_or(true);
let hook = Hook { capture_backtrace };
eyre::set_hook(Box::new(move |e| Box::new(hook.make_handler(e))))
}
struct Hook {
capture_backtrace: bool,
}
impl Hook {
fn make_handler(&self, _error: &(dyn Error + 'static)) -> Handler {
let backtrace = if self.capture_backtrace {
Some(Backtrace::new())
} else {
None
};
Handler {
backtrace,
custom_msg: None,
}
}
}
struct Handler {
// custom configured backtrace capture
backtrace: Option<Backtrace>,
// customizable message payload associated with reports
custom_msg: Option<&'static str>,
}
impl EyreHandler for Handler {
fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
return fmt::Debug::fmt(error, f);
}
let errors = iter::successors(Some(error), |error| error.source());
for (ind, error) in errors.enumerate() {
write!(f, "\n{:>4}: {}", ind, error)?;
}
if let Some(backtrace) = self.backtrace.as_ref() {
writeln!(f, "\n\nBacktrace:\n{:?}", backtrace)?;
}
if let Some(msg) = self.custom_msg.as_ref() {
writeln!(f, "\n\n{}", msg)?;
}
Ok(())
}
}

View File

@ -1,23 +1,15 @@
use self::ChainState::*;
use crate::StdError;
#[cfg(feature = "std")]
use std::vec;
#[cfg(feature = "std")]
pub(crate) use crate::Chain;
#[cfg(not(feature = "std"))]
pub(crate) struct Chain<'a> {
state: ChainState<'a>,
}
#[derive(Clone)]
pub(crate) enum ChainState<'a> {
Linked {
next: Option<&'a (dyn StdError + 'static)>,
},
#[cfg(feature = "std")]
Buffered {
rest: vec::IntoIter<&'a (dyn StdError + 'static)>,
},
@ -61,7 +53,6 @@ impl<'a> Iterator for Chain<'a> {
*next = error.source();
Some(error)
}
#[cfg(feature = "std")]
Buffered { rest } => rest.next(),
}
}
@ -72,7 +63,6 @@ impl<'a> Iterator for Chain<'a> {
}
}
#[cfg(feature = "std")]
impl DoubleEndedIterator for Chain<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
match &mut self.state {
@ -103,13 +93,11 @@ impl ExactSizeIterator for Chain<'_> {
}
len
}
#[cfg(feature = "std")]
Buffered { rest } => rest.len(),
}
}
}
#[cfg(feature = "std")]
impl Default for Chain<'_> {
fn default() -> Self {
Chain {

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,17 @@ 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 +26,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 +36,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 +55,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 +71,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 +87,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 +140,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 +172,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

@ -1,4 +1,3 @@
use crate::alloc::Box;
use crate::chain::Chain;
use crate::EyreHandler;
use crate::{Report, StdError};
@ -7,13 +6,9 @@ use core::fmt::{self, Debug, Display};
use core::mem::{self, ManuallyDrop};
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`
@ -21,8 +16,6 @@ where
///
/// If the error type does not provide a backtrace, a backtrace will be
/// created here to ensure that a backtrace exists.
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn new<E>(error: E) -> Self
where
E: StdError + Send + Sync + 'static,
@ -74,23 +67,22 @@ where
Report::from_adhoc(message)
}
#[cfg(feature = "std")]
pub(crate) fn from_std<E>(error: E) -> Self
where
E: StdError + Send + Sync + 'static,
{
let vtable = &ErrorVTable {
object_drop: object_drop::<E, H>,
object_ref: object_ref::<E, H>,
#[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_drop: object_drop::<E>,
object_ref: object_ref::<E>,
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 +93,18 @@ 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>,
#[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_drop: object_drop::<MessageError<M>>,
object_ref: object_ref::<MessageError<M>>,
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,22 +115,21 @@ 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>,
#[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_drop: object_drop::<DisplayError<M>>,
object_ref: object_ref::<DisplayError<M>>,
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) }
}
#[cfg(feature = "std")]
pub(crate) fn from_msg<D, E>(msg: D, error: E) -> Self
where
D: Display + Send + Sync + 'static,
@ -147,33 +138,32 @@ 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>,
#[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_drop: object_drop::<ContextError<D, E>>,
object_ref: object_ref::<ContextError<D, E>>,
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) }
}
#[cfg(feature = "std")]
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>,
#[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_drop: object_drop::<BoxedError>,
object_ref: object_ref::<BoxedError>,
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 +176,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 +195,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 +258,15 @@ 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>,
#[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_drop: object_drop::<ContextError<D, Report>>,
object_ref: object_ref::<ContextError<D, Report>>,
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.
@ -301,7 +294,6 @@ where
/// None
/// }
/// ```
#[cfg(feature = "std")]
pub fn chain(&self) -> Chain<'_> {
self.inner.chain()
}
@ -311,7 +303,6 @@ where
///
/// The root cause is the last error in the iterator produced by
/// [`chain()`][Report::chain].
#[cfg(feature = "std")]
pub fn root_cause(&self) -> &(dyn StdError + 'static) {
let mut chain = self.chain();
let mut root_cause = chain.next().unwrap();
@ -432,32 +423,30 @@ 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 {
@ -465,8 +454,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 {
@ -474,29 +462,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 +493,70 @@ 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),
#[cfg(feature = "std")]
object_mut: unsafe fn(&mut ErrorImpl<(), H>) -> &mut (dyn StdError + Send + Sync + 'static),
struct ErrorVTable {
object_drop: unsafe fn(Box<ErrorImpl<()>>),
object_ref: unsafe fn(&ErrorImpl<()>) -> &(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 {
@ -597,19 +565,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 {
@ -618,10 +584,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 +593,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 +653,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,29 +669,22 @@ 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.
unsafe { &*(self.vtable.object_ref)(self) }
}
#[cfg(feature = "std")]
pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
// Use vtable to attach E's native StdError vtable for the right
// original type E.
@ -746,9 +696,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 +705,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 +714,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 +723,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 +739,19 @@ 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,10 +45,9 @@
// let error = $msg;
// (&error).eyre_kind().new(error)
use crate::{EyreHandler, Report};
use crate::Report;
use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
use crate::StdError;
pub struct Adhoc;
@ -63,7 +62,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,36 +70,28 @@ 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()
}
}
#[cfg(feature = "std")]
pub struct Boxed;
#[cfg(feature = "std")]
pub trait BoxedKind: Sized {
#[inline]
fn eyre_kind(&self) -> Boxed {
@ -108,47 +99,10 @@ pub trait BoxedKind: Sized {
}
}
#[cfg(feature = "std")]
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)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::eyre;
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),
_f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
Ok(())
}
}
fn _parse(s: &str) -> Result<i32, ParseIntError> {
s.parse::<i32>()
}
fn _throw_error() -> Result<(), Report<NonDefaultHandler>> {
match _parse("abc") {
Ok(_) => Ok(()),
Err(e) => Err(eyre!(e).wrap_err("try parsing an actual number")),
}
}
}

View File

@ -2,16 +2,16 @@
//! error handling type for easy idiomatic error handling and reporting in Rust
//! applications.
//!
//! This crate is a fork of [`anyhow`] by @dtolnay with a support for customized
//! `Reports`. For more details on customization checkout the docs on
//! This crate is a fork of [`anyhow`] with a support for customized
//! error reports. For more details on customization checkout the docs on
//! [`eyre::EyreHandler`].
//!
//! ## Custom Report Handlers
//!
//! The heart of this crate is its ability to swap out the Handler type to change
//! The heart of this crate is it's ability to swap out the Handler type to change
//! what information is carried alongside errors and how the end report is
//! formatted. This crate is meant to be used alongside companion crates that
//! customize its behavior. Below is a list of known crates that export report
//! customize it's behavior. Below is a list of known crates that export report
//! handlers for eyre and short summaries of what features they provide.
//!
//! - [`stable-eyre`]: Switches the backtrace type from `std`'s to `backtrace-rs`'s
@ -21,11 +21,11 @@
//! `tracing_error::SpanTrace`. Provides a `Help` trait for attaching warnings
//! and suggestions to error reports. The end report is then pretty printed with
//! the help of [`color-backtrace`], [`color-spantrace`], and `ansi_term`. Check
//! out the README on [`color-eyre`] for screenshots of the report format.
//! out the README on [`color-eyre`] for details on the report format.
//! - [`simple-eyre`]: A minimal `EyreHandler` that captures no additional
//! information, for when you do not wish to capture `Backtrace`s with errors.
//! - [`jane-eyre`]: A a report handler crate that exists purely for the pun.
//! Currently just re-exports `color-eyre`.
//! - [`jane-eyre`]: A report handler crate that exists purely for the pun.
//! Currently just re-exports `color-eyre`.
//!
//! ## Details
//!
@ -206,7 +206,7 @@
//!
//! ```toml
//! [dependencies]
//! eyre = { version = "0.5", default-features = false }
//! eyre = { version = "0.6", default-features = false }
//! ```
//!
//! Since the `?`-based error conversions would normally rely on the
@ -225,10 +225,11 @@
//!
//! ## Comparison to thiserror
//!
//! Use Eyre if you don't care what error type your functions return, you just
//! want it to be easy. This is common in application code. Use [thiserror] if you
//! are a library that wants to design your own dedicated error type(s) so that on
//! failures the caller gets exactly the information that you choose.
//! Use `eyre` if you don't think you'll do anything with an error other than
//! report it. This is common in application code. Use `thiserror` if you think
//! you need an error type that can be handled via match or reported. This is
//! common in library crates where you don't know how your users will handle
//! your errors.
//!
//! [thiserror]: https://github.com/dtolnay/thiserror
//!
@ -236,42 +237,7 @@
//!
//! This crate does its best to be usable as a drop in replacement of `anyhow` and
//! vice-versa by `re-exporting` all of the renamed APIs with the names used in
//! `anyhow`.
//!
//! There are two main incompatibilities that you might encounter when porting a
//! codebase from `anyhow` to `eyre`:
//!
//! - type inference errors when using `eyre!`
//! - `.context` not being implemented for `Option`
//!
//! #### Type Inference Errors
//!
//! The type inference issue is caused by the generic parameter, which isn't
//! present in `anyhow::Error`. Specifically, the following works in anyhow:
//!
//! ```rust
//! # fn get_optional_val() -> Option<()> { None };
//! use anyhow::anyhow;
//!
//! // Works
//! let val = get_optional_val().ok_or_else(|| anyhow!("failed to get value")).unwrap_err();
//! ```
//!
//! Where as with `eyre!` this will fail due to being unable to infer the type for
//! the Handler parameter. The solution to this problem, should you encounter it,
//! is to give the compiler a hint for what type it should be resolving to, either
//! via your return type or a type annotation.
//!
//! ```rust,compile_fail
//! use eyre::eyre;
//!
//! # fn get_optional_val() -> Option<()> { None };
//! // Broken
//! let val = get_optional_val().ok_or_else(|| eyre!("failed to get value")).unwrap();
//!
//! // Works
//! let val: Report = get_optional_val().ok_or_else(|| eyre!("failed to get value")).unwrap();
//! ```
//! `anyhow`, though there are some differences still.
//!
//! #### `Context` and `Option`
//!
@ -301,7 +267,7 @@
//! let result: Result<()> = opt.ok_or_else(|| eyre!("new error message"));
//! ```
//!
//! However, to help with porting we do provide a `ContextCompat` trait which
//! **NOTE**: However, to help with porting we do provide a `ContextCompat` trait which
//! implements `context` for options which you can import to make existing
//! `.context` calls compile.
//!
@ -317,7 +283,7 @@
//! [`simple-eyre`]: https://github.com/yaahc/simple-eyre
//! [`color-spantrace`]: https://github.com/yaahc/color-spantrace
//! [`color-backtrace`]: https://github.com/athre0z/color-backtrace
#![doc(html_root_url = "https://docs.rs/eyre/0.5.0")]
#![doc(html_root_url = "https://docs.rs/eyre/0.6.0")]
#![warn(
missing_debug_implementations,
missing_docs,
@ -343,30 +309,12 @@
)]
#![cfg_attr(backtrace, feature(backtrace))]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(
clippy::needless_doctest_main,
clippy::new_ret_no_self,
clippy::wrong_self_convention
)]
mod alloc {
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
pub(crate) use alloc::boxed::Box;
#[cfg(feature = "std")]
pub(crate) use std::boxed::Box;
#[cfg(not(feature = "std"))]
pub(crate) use alloc::string::String;
// #[cfg(feature = "std")]
// pub(crate) use std::string::String;
}
#[macro_use]
mod backtrace;
mod chain;
@ -377,28 +325,17 @@ mod kind;
mod macros;
mod wrapper;
use crate::alloc::Box;
use crate::backtrace::Backtrace;
use crate::error::ErrorImpl;
use core::fmt::Display;
use core::mem::ManuallyDrop;
#[cfg(not(feature = "std"))]
use core::fmt::Debug;
#[cfg(feature = "std")]
use std::error::Error as StdError;
#[cfg(not(feature = "std"))]
pub trait StdError: Debug + Display {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
None
}
}
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)]
@ -478,151 +415,174 @@ pub use WrapErr as Context;
/// ```
///
/// If none of the built-in representations are appropriate and you would prefer
/// to render the error and its cause chain yourself, it can be done something
/// like this:
/// to render the error and its cause chain yourself, it can be done by defining
/// your own [`EyreHandler`] and [`hook`] to use it.
///
/// ```
/// use eyre::{WrapErr, Result};
/// [`EyreHandler`]: trait.EyreHandler.html
/// [`hook`]: fn.set_hook.html
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();
/// Error indicating that `set_hook` was unable to install the provided ErrorHook
#[derive(Debug)]
pub struct InstallError;
impl core::fmt::Display for InstallError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("cannot install provided ErrorHook, a hook has already been installed")
}
}
impl StdError for InstallError {}
/// Install the provided error hook for constructing EyreHandlers when converting
/// Errors to Reports
///
/// fn main() {
/// if let Err(err) = try_main() {
/// eprintln!("ERROR: {}", err);
/// err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause));
/// std::process::exit(1);
/// # Details
///
/// To customize the format and content of error reports from `eyre` you must
/// first define a new `EyreHandler` type to capture and store the extra context
/// and to define the format of how to display the chain of errors and this
/// stored context. Once this type has been defined you must also define a global
/// hook used to construct these handlers whenever `Report`s are constructed.
///
/// # Examples
///
/// ```rust,should_panic
/// use backtrace::Backtrace;
/// use eyre::EyreHandler;
/// use std::error::Error;
/// use std::{fmt, iter};
///
/// fn main() -> eyre::Result<()> {
/// // Install our custom eyre report hook for constructing our custom Handlers
/// install().unwrap();
///
/// // construct a report with, hopefully, our custom handler!
/// let mut report = eyre::eyre!("hello from custom error town!");
///
/// // manually set the custom msg for this report after it has been constructed
/// if let Some(handler) = report.handler_mut().downcast_mut::<Handler>() {
/// handler.custom_msg = Some("you're the best users, you know that right???");
/// }
///
/// // print that shit!!
/// Err(report)
/// }
///
/// // define a handler that captures backtraces unless told not to
/// fn install() -> Result<(), impl Error> {
/// let capture_backtrace = std::env::var("RUST_BACKWARDS_TRACE")
/// .map(|val| val != "0")
/// .unwrap_or(true);
///
/// let hook = Hook { capture_backtrace };
///
/// eyre::set_hook(Box::new(move |e| Box::new(hook.make_handler(e))))
/// }
///
/// struct Hook {
/// capture_backtrace: bool,
/// }
///
/// impl Hook {
/// fn make_handler(&self, _error: &(dyn Error + 'static)) -> Handler {
/// let backtrace = if self.capture_backtrace {
/// Some(Backtrace::new())
/// } else {
/// None
/// };
///
/// Handler {
/// backtrace,
/// custom_msg: None,
/// }
/// }
/// }
///
/// fn try_main() -> Result<()> {
/// # const IGNORE: &str = stringify! {
/// ...
/// # };
/// # Ok(())
/// }
/// ```
pub struct Report<H = DefaultHandler>
where
H: EyreHandler,
{
inner: ManuallyDrop<Box<ErrorImpl<(), H>>>,
}
/// Error Report Handler trait for customizing `eyre::Report`
///
/// ## Customization
///
/// In order to insert your own custom context and report format you must first
/// implement the `eyre::EyreHandler` trait.
///
/// Once you've defined a custom Handler type you can use it throughout your
/// application by defining a type alias.
///
/// ```rust
/// use backtrace::Backtrace;
/// use eyre::EyreHandler;
/// # use eyre::Chain;
/// # use std::error::Error;
/// use indenter::indented;
///
/// pub struct Handler {
/// backtrace: Backtrace,
/// struct Handler {
/// // custom configured backtrace capture
/// backtrace: Option<Backtrace>,
/// // customizable message payload associated with reports
/// custom_msg: Option<&'static str>,
/// }
///
/// 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!(indented(f).ind(n), "{}", error)?;
/// # } else {
/// # write!(indented(f), "{}", error)?;
/// # }
/// # }
/// # }
/// # let backtrace = &self.backtrace;
/// # write!(f, "\n\nStack backtrace:\n{:?}", backtrace)?;
/// # Ok(())
/// # }
/// }
/// fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// if f.alternate() {
/// return fmt::Debug::fmt(error, f);
/// }
///
/// type Report = eyre::Report<Handler>;
/// type Result<T, E = eyre::Report<Handler>> = core::result::Result<T, E>;
/// let errors = iter::successors(Some(error), |error| error.source());
///
/// for (ind, error) in errors.enumerate() {
/// write!(f, "\n{:>4}: {}", ind, error)?;
/// }
///
/// if let Some(backtrace) = self.backtrace.as_ref() {
/// writeln!(f, "\n\nBacktrace:\n{:?}", backtrace)?;
/// }
///
/// if let Some(msg) = self.custom_msg.as_ref() {
/// writeln!(f, "\n\n{}", msg)?;
/// }
///
/// Ok(())
/// }
/// }
/// ```
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 fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
HOOK.set(hook).map_err(|_| InstallError)
}
fn capture_handler(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
let hook = HOOK
.get_or_init(|| Box::new(DefaultHandler::default_with))
.as_ref();
hook(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`
pub trait EyreHandler: core::any::Any + Send + Sync {
/// Define the report format
///
/// Used to override the report format of `eyre::Report`
@ -641,13 +601,6 @@ pub trait EyreHandler: Sized + Send + Sync + 'static {
/// }
///
/// 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),
@ -715,6 +668,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 +692,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),
@ -800,14 +755,13 @@ impl EyreHandler for DefaultHandler {
/// None
/// }
/// ```
#[cfg(feature = "std")]
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct Chain<'a> {
state: crate::chain::ChainState<'a>,
}
/// `Result<T, Error>`
/// type alias for `Result<T, Report>`
///
/// This is a reasonable return type to use throughout your application but also for `fn main`; if
/// you do, failures will be printed along with a backtrace if one was captured.
@ -855,7 +809,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 +976,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 +1044,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,25 +1073,20 @@ 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)]
pub mod kind {
pub use crate::kind::{AdhocKind, TraitKind};
#[cfg(feature = "std")]
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

@ -63,25 +63,21 @@ impl Display for NoneError {
impl StdError for NoneError {}
#[cfg(feature = "std")]
#[repr(transparent)]
pub(crate) struct BoxedError(pub(crate) Box<dyn StdError + Send + Sync>);
#[cfg(feature = "std")]
impl Debug for BoxedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
#[cfg(feature = "std")]
impl Display for BoxedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[cfg(feature = "std")]
impl StdError for BoxedError {
#[cfg(backtrace)]
fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> {

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();

View File

@ -4,18 +4,18 @@ error[E0599]: no method named `eyre_kind` found for reference `&Error` in the cu
4 | struct Error;
| -------------
| |
| doesn't satisfy `Error: eyre::kind::TraitKind<_>`
| doesn't satisfy `Error: std::convert::Into<eyre::Report<_>>`
| doesn't satisfy `Error: eyre::kind::TraitKind`
| doesn't satisfy `Error: std::convert::Into<eyre::Report>`
| doesn't satisfy `Error: std::fmt::Display`
...
7 | let _ = eyre!(Error);
| ^^^^^^^^^^^^ method not found in `&Error`
|
= note: the method `eyre_kind` exists but the following trait bounds were not satisfied:
`Error: std::convert::Into<eyre::Report<_>>`
which is required by `Error: eyre::kind::TraitKind<_>`
`Error: std::convert::Into<eyre::Report>`
which is required by `Error: eyre::kind::TraitKind`
`Error: std::fmt::Display`
which is required by `&Error: eyre::kind::AdhocKind`
`&Error: std::convert::Into<eyre::Report<_>>`
which is required by `&Error: eyre::kind::TraitKind<_>`
`&Error: std::convert::Into<eyre::Report>`
which is required by `&Error: eyre::kind::TraitKind`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)