mirror of
https://github.com/eyre-rs/eyre.git
synced 2025-09-27 13:01:29 +00:00
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:
parent
96ddb39cd0
commit
f39eaff0b2
@ -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"]
|
||||
|
67
README.md
67
README.md
@ -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.
|
||||
|
||||
|
3
build.rs
3
build.rs
@ -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"),
|
||||
_ => {}
|
||||
|
81
examples/custom_handler.rs
Normal file
81
examples/custom_handler.rs
Normal 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(())
|
||||
}
|
||||
}
|
12
src/chain.rs
12
src/chain.rs
@ -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 {
|
||||
|
@ -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> {}
|
||||
}
|
||||
|
293
src/error.rs
293
src/error.rs
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
68
src/kind.rs
68
src/kind.rs
@ -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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
451
src/lib.rs
451
src/lib.rs
@ -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)
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user