Skip to content

Commit de939a5

Browse files
committed
feat: support converting between anyhow::Error and eyre::Result
1 parent df42dc4 commit de939a5

File tree

4 files changed

+82
-7
lines changed

4 files changed

+82
-7
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ indenter = "0.3.0"
1818
once_cell = "1.18.0"
1919
owo-colors = "4.0"
2020
autocfg = "1.0"
21+
anyhow = "1.0"
2122

2223
[profile.dev.package.backtrace]
2324
opt-level = 3

eyre/Cargo.toml

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ readme = { workspace = true }
1313
rust-version = { workspace = true }
1414

1515
[features]
16-
default = ["anyhow", "auto-install", "track-caller"]
17-
anyhow = []
16+
default = [ "auto-install", "track-caller"]
1817
auto-install = []
1918
track-caller = []
2019

2120
[dependencies]
2221
indenter = { workspace = true }
2322
once_cell = { workspace = true }
2423
pyo3 = { version = "0.20", optional = true, default-features = false }
24+
anyhow = { workspace = true, optional = true, default-features = false }
2525

2626
[build-dependencies]
2727
autocfg = { workspace = true }
@@ -32,7 +32,6 @@ rustversion = "1.0"
3232
thiserror = "1.0"
3333
trybuild = { version = "=1.0.89", features = ["diff"] } # pinned due to MSRV
3434
backtrace = "0.3.46"
35-
anyhow = "1.0.28"
3635
syn = { version = "2.0", features = ["full"] }
3736
pyo3 = { version = "0.20", default-features = false, features = ["auto-initialize"] }
3837

eyre/src/error.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use core::mem::{self, ManuallyDrop};
88
use core::ptr::{self, NonNull};
99

1010
use core::ops::{Deref, DerefMut};
11+
use std::any::Any;
1112

1213
impl Report {
1314
/// Create a new error object from any error type.
@@ -490,14 +491,33 @@ impl Report {
490491

491492
impl<E> From<E> for Report
492493
where
493-
E: StdError + Send + Sync + 'static,
494+
E: 'static + Into<anyhow::Error>,
495+
Result<(), E>: anyhow::Context<(), E>,
494496
{
495-
#[cfg_attr(track_caller, track_caller)]
496-
fn from(error: E) -> Self {
497-
Report::from_std(error)
497+
fn from(value: E) -> Self {
498+
let mut value = Some(value);
499+
let e = &mut value as &mut dyn Any;
500+
501+
if let Some(e) = e.downcast_mut::<Option<anyhow::Error>>() {
502+
let e: Box<dyn StdError + Send + Sync> = e.take().unwrap().into();
503+
Report::from_boxed(e)
504+
} else {
505+
let e: Box<dyn StdError + Send + Sync> = value.take().unwrap().into().into();
506+
Report::from_boxed(e)
507+
}
498508
}
499509
}
500510

511+
// impl<E> From<E> for Report
512+
// where
513+
// E: StdError + Send + Sync + 'static,
514+
// {
515+
// #[cfg_attr(track_caller, track_caller)]
516+
// fn from(error: E) -> Self {
517+
// Report::from_std(error)
518+
// }
519+
// }
520+
501521
impl Deref for Report {
502522
type Target = dyn StdError + Send + Sync + 'static;
503523

eyre/tests/test_anyhow.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use eyre::Report;
2+
use std::fmt::Display;
3+
4+
#[derive(Debug)]
5+
struct RootError;
6+
7+
impl Display for RootError {
8+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9+
write!(f, "RootError")
10+
}
11+
}
12+
13+
impl std::error::Error for RootError {
14+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
15+
None
16+
}
17+
}
18+
19+
fn this_function_fails() -> anyhow::Result<()> {
20+
use anyhow::Context;
21+
22+
Err(RootError).context("Ouch!").context("Anyhow context A")
23+
}
24+
25+
fn bubble() -> eyre::Result<()> {
26+
use anyhow::Context;
27+
this_function_fails().context("Anyhow context B")?;
28+
29+
Ok(())
30+
}
31+
32+
#[test]
33+
fn anyhow_conversion() {
34+
use eyre::WrapErr;
35+
let error: Report = bubble().wrap_err("Eyre context").unwrap_err();
36+
37+
eprintln!("Error: {:?}", error);
38+
39+
let chain = error.chain().map(ToString::to_string).collect::<Vec<_>>();
40+
assert_eq!(
41+
chain,
42+
[
43+
"Eyre context",
44+
// Anyhow context
45+
"Anyhow context B",
46+
"Anyhow context A",
47+
// Anyhow error
48+
"Ouch!",
49+
// Original concrete error, shows up in chain too
50+
"RootError"
51+
]
52+
);
53+
54+
// let error = Report::msg("A").wrap_err("B").wrap_err("C");
55+
}

0 commit comments

Comments
 (0)