mirror of
https://github.com/eyre-rs/eyre.git
synced 2025-09-27 04:50:50 +00:00
feat: convert between anyhow::Error and eyre::Result
This commit is contained in:
parent
df42dc4338
commit
0668083dce
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -184,6 +184,7 @@ name = "eyre"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"autocfg",
|
||||
"backtrace",
|
||||
"futures",
|
||||
"indenter",
|
||||
|
@ -18,6 +18,7 @@ indenter = "0.3.0"
|
||||
once_cell = "1.18.0"
|
||||
owo-colors = "4.0"
|
||||
autocfg = "1.0"
|
||||
anyhow = "1.0"
|
||||
|
||||
[profile.dev.package.backtrace]
|
||||
opt-level = 3
|
||||
|
@ -13,8 +13,7 @@ readme = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["anyhow", "auto-install", "track-caller"]
|
||||
anyhow = []
|
||||
default = [ "auto-install", "track-caller"]
|
||||
auto-install = []
|
||||
track-caller = []
|
||||
|
||||
@ -22,17 +21,18 @@ track-caller = []
|
||||
indenter = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
pyo3 = { version = "0.20", optional = true, default-features = false }
|
||||
anyhow = { workspace = true, optional = true, default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
autocfg = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true, default-features = true }
|
||||
futures = { version = "0.3", default-features = false }
|
||||
rustversion = "1.0"
|
||||
thiserror = "1.0"
|
||||
trybuild = { version = "=1.0.89", features = ["diff"] } # pinned due to MSRV
|
||||
backtrace = "0.3.46"
|
||||
anyhow = "1.0.28"
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
pyo3 = { version = "0.20", default-features = false, features = ["auto-initialize"] }
|
||||
|
||||
|
@ -17,13 +17,16 @@ macro_rules! capture_backtrace {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
/// Capture a backtrace iff there is not already a backtrace in the error chain
|
||||
#[cfg(generic_member_access)]
|
||||
macro_rules! backtrace_if_absent {
|
||||
($err:expr) => {
|
||||
match std::error::request_ref::<std::backtrace::Backtrace>($err as &dyn std::error::Error) {
|
||||
Some(_) => None,
|
||||
None => capture_backtrace!(),
|
||||
Some(v) => None,
|
||||
None => {
|
||||
capture_backtrace!()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -150,6 +150,11 @@ impl<D> StdError for ContextError<D, Report>
|
||||
where
|
||||
D: Display,
|
||||
{
|
||||
#[cfg(generic_member_access)]
|
||||
fn provide<'a>(&'a self, request: &mut std::error::Request<'a>) {
|
||||
self.error.provide(request)
|
||||
}
|
||||
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
Some(ErrorImpl::error(self.error.inner.as_ref()))
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use core::mem::{self, ManuallyDrop};
|
||||
use core::ptr::{self, NonNull};
|
||||
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use std::any::Any;
|
||||
|
||||
impl Report {
|
||||
/// Create a new error object from any error type.
|
||||
@ -488,6 +489,7 @@ impl Report {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "anyhow"))]
|
||||
impl<E> From<E> for Report
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
@ -498,6 +500,27 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "anyhow")]
|
||||
impl<E> From<E> for Report
|
||||
where
|
||||
E: 'static + Into<anyhow::Error>,
|
||||
Result<(), E>: anyhow::Context<(), E>,
|
||||
{
|
||||
#[cfg_attr(track_caller, track_caller)]
|
||||
fn from(value: E) -> Self {
|
||||
let mut value = Some(value);
|
||||
let e = &mut value as &mut dyn Any;
|
||||
|
||||
if let Some(e) = e.downcast_mut::<Option<anyhow::Error>>() {
|
||||
let e: Box<dyn StdError + Send + Sync> = e.take().unwrap().into();
|
||||
Report::from_boxed(e)
|
||||
} else {
|
||||
let e: Box<dyn StdError + Send + Sync> = value.take().unwrap().into().into();
|
||||
Report::from_boxed(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Report {
|
||||
type Target = dyn StdError + Send + Sync + 'static;
|
||||
|
||||
|
@ -381,7 +381,7 @@ use crate::backtrace::Backtrace;
|
||||
use crate::error::ErrorImpl;
|
||||
use core::fmt::{Debug, Display};
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::{any::Any, error::Error as StdError};
|
||||
|
||||
pub use eyre as format_err;
|
||||
/// Compatibility re-export of `eyre` for interop with `anyhow`
|
||||
@ -779,6 +779,7 @@ impl DefaultHandler {
|
||||
#[cfg_attr(not(feature = "auto-install"), allow(dead_code))]
|
||||
pub fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
|
||||
// Capture the backtrace if the source error did not already capture one
|
||||
eprintln!("checking backtrace");
|
||||
let backtrace = backtrace_if_absent!(error);
|
||||
|
||||
Box::new(Self {
|
||||
@ -848,7 +849,10 @@ impl EyreHandler for DefaultHandler {
|
||||
let backtrace = self
|
||||
.backtrace
|
||||
.as_ref()
|
||||
.or_else(|| std::error::request_ref::<Backtrace>(error))
|
||||
.or_else(|| {
|
||||
eprintln!("Requesting backtrace from underlying type");
|
||||
std::error::request_ref::<std::backtrace::Backtrace>(error)
|
||||
})
|
||||
.expect("backtrace capture failed");
|
||||
|
||||
if let BacktraceStatus::Captured = backtrace.status() {
|
||||
|
59
eyre/tests/test_anyhow.rs
Normal file
59
eyre/tests/test_anyhow.rs
Normal file
@ -0,0 +1,59 @@
|
||||
#![cfg(generic_member_access)]
|
||||
#![feature(error_generic_member_access)]
|
||||
|
||||
use eyre::Report;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RootError;
|
||||
|
||||
impl Display for RootError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "RootError")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RootError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn this_function_fails() -> anyhow::Result<()> {
|
||||
use anyhow::Context;
|
||||
|
||||
Err(RootError).context("Ouch!").context("Anyhow context A")
|
||||
}
|
||||
|
||||
fn test_failure() -> eyre::Result<()> {
|
||||
use anyhow::Context;
|
||||
this_function_fails().context("Anyhow context B")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anyhow_conversion() {
|
||||
use eyre::WrapErr;
|
||||
let error: Report = test_failure().wrap_err("Eyre context").unwrap_err();
|
||||
|
||||
eprintln!("Error: {:?}", error);
|
||||
|
||||
let chain = error.chain().map(ToString::to_string).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
chain,
|
||||
[
|
||||
"Eyre context",
|
||||
// Anyhow context
|
||||
"Anyhow context B",
|
||||
"Anyhow context A",
|
||||
// Anyhow error
|
||||
"Ouch!",
|
||||
// Original concrete error, shows up in chain too
|
||||
"RootError"
|
||||
]
|
||||
);
|
||||
|
||||
let backtrace = std::error::request_ref::<std::backtrace::Backtrace>(&*error).unwrap();
|
||||
dbg!(backtrace);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user