mirror of
https://github.com/eyre-rs/eyre.git
synced 2025-09-27 13:01:29 +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"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"autocfg",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"futures",
|
"futures",
|
||||||
"indenter",
|
"indenter",
|
||||||
|
@ -18,6 +18,7 @@ indenter = "0.3.0"
|
|||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
owo-colors = "4.0"
|
owo-colors = "4.0"
|
||||||
autocfg = "1.0"
|
autocfg = "1.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
||||||
[profile.dev.package.backtrace]
|
[profile.dev.package.backtrace]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
@ -13,8 +13,7 @@ readme = { workspace = true }
|
|||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["anyhow", "auto-install", "track-caller"]
|
default = [ "auto-install", "track-caller"]
|
||||||
anyhow = []
|
|
||||||
auto-install = []
|
auto-install = []
|
||||||
track-caller = []
|
track-caller = []
|
||||||
|
|
||||||
@ -22,17 +21,18 @@ track-caller = []
|
|||||||
indenter = { workspace = true }
|
indenter = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
pyo3 = { version = "0.20", optional = true, default-features = false }
|
pyo3 = { version = "0.20", optional = true, default-features = false }
|
||||||
|
anyhow = { workspace = true, optional = true, default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
autocfg = { workspace = true }
|
autocfg = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
anyhow = { workspace = true, default-features = true }
|
||||||
futures = { version = "0.3", default-features = false }
|
futures = { version = "0.3", default-features = false }
|
||||||
rustversion = "1.0"
|
rustversion = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
trybuild = { version = "=1.0.89", features = ["diff"] } # pinned due to MSRV
|
trybuild = { version = "=1.0.89", features = ["diff"] } # pinned due to MSRV
|
||||||
backtrace = "0.3.46"
|
backtrace = "0.3.46"
|
||||||
anyhow = "1.0.28"
|
|
||||||
syn = { version = "2.0", features = ["full"] }
|
syn = { version = "2.0", features = ["full"] }
|
||||||
pyo3 = { version = "0.20", default-features = false, features = ["auto-initialize"] }
|
pyo3 = { version = "0.20", default-features = false, features = ["auto-initialize"] }
|
||||||
|
|
||||||
|
@ -17,13 +17,16 @@ macro_rules! capture_backtrace {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Capture a backtrace iff there is not already a backtrace in the error chain
|
/// Capture a backtrace iff there is not already a backtrace in the error chain
|
||||||
#[cfg(generic_member_access)]
|
#[cfg(generic_member_access)]
|
||||||
macro_rules! backtrace_if_absent {
|
macro_rules! backtrace_if_absent {
|
||||||
($err:expr) => {
|
($err:expr) => {
|
||||||
match std::error::request_ref::<std::backtrace::Backtrace>($err as &dyn std::error::Error) {
|
match std::error::request_ref::<std::backtrace::Backtrace>($err as &dyn std::error::Error) {
|
||||||
Some(_) => None,
|
Some(v) => None,
|
||||||
None => capture_backtrace!(),
|
None => {
|
||||||
|
capture_backtrace!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -150,6 +150,11 @@ impl<D> StdError for ContextError<D, Report>
|
|||||||
where
|
where
|
||||||
D: Display,
|
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)> {
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
Some(ErrorImpl::error(self.error.inner.as_ref()))
|
Some(ErrorImpl::error(self.error.inner.as_ref()))
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use core::mem::{self, ManuallyDrop};
|
|||||||
use core::ptr::{self, NonNull};
|
use core::ptr::{self, NonNull};
|
||||||
|
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
/// Create a new error object from any error type.
|
/// 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
|
impl<E> From<E> for Report
|
||||||
where
|
where
|
||||||
E: StdError + Send + Sync + 'static,
|
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 {
|
impl Deref for Report {
|
||||||
type Target = dyn StdError + Send + Sync + 'static;
|
type Target = dyn StdError + Send + Sync + 'static;
|
||||||
|
|
||||||
|
@ -381,7 +381,7 @@ use crate::backtrace::Backtrace;
|
|||||||
use crate::error::ErrorImpl;
|
use crate::error::ErrorImpl;
|
||||||
use core::fmt::{Debug, Display};
|
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;
|
pub use eyre as format_err;
|
||||||
/// Compatibility re-export of `eyre` for interop with `anyhow`
|
/// Compatibility re-export of `eyre` for interop with `anyhow`
|
||||||
@ -779,6 +779,7 @@ impl DefaultHandler {
|
|||||||
#[cfg_attr(not(feature = "auto-install"), allow(dead_code))]
|
#[cfg_attr(not(feature = "auto-install"), allow(dead_code))]
|
||||||
pub fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
|
pub fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
|
||||||
// Capture the backtrace if the source error did not already capture one
|
// Capture the backtrace if the source error did not already capture one
|
||||||
|
eprintln!("checking backtrace");
|
||||||
let backtrace = backtrace_if_absent!(error);
|
let backtrace = backtrace_if_absent!(error);
|
||||||
|
|
||||||
Box::new(Self {
|
Box::new(Self {
|
||||||
@ -848,7 +849,10 @@ impl EyreHandler for DefaultHandler {
|
|||||||
let backtrace = self
|
let backtrace = self
|
||||||
.backtrace
|
.backtrace
|
||||||
.as_ref()
|
.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");
|
.expect("backtrace capture failed");
|
||||||
|
|
||||||
if let BacktraceStatus::Captured = backtrace.status() {
|
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