From de939a5334f54da91567f12757e4dd92ac9847e4 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Fri, 21 Jun 2024 19:58:28 +0200 Subject: [PATCH] feat: support converting between anyhow::Error and eyre::Result --- Cargo.toml | 1 + eyre/Cargo.toml | 5 ++-- eyre/src/error.rs | 28 +++++++++++++++++--- eyre/tests/test_anyhow.rs | 55 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 eyre/tests/test_anyhow.rs diff --git a/Cargo.toml b/Cargo.toml index c1651b2..68ac32e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/eyre/Cargo.toml b/eyre/Cargo.toml index 3c779ef..a3c531e 100644 --- a/eyre/Cargo.toml +++ b/eyre/Cargo.toml @@ -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,6 +21,7 @@ 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 } @@ -32,7 +32,6 @@ 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"] } diff --git a/eyre/src/error.rs b/eyre/src/error.rs index 511b4c4..fc820fd 100644 --- a/eyre/src/error.rs +++ b/eyre/src/error.rs @@ -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. @@ -490,14 +491,33 @@ impl Report { impl From for Report where - E: StdError + Send + Sync + 'static, + E: 'static + Into, + Result<(), E>: anyhow::Context<(), E>, { - #[cfg_attr(track_caller, track_caller)] - fn from(error: E) -> Self { - Report::from_std(error) + 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::>() { + let e: Box = e.take().unwrap().into(); + Report::from_boxed(e) + } else { + let e: Box = value.take().unwrap().into().into(); + Report::from_boxed(e) + } } } +// impl From for Report +// where +// E: StdError + Send + Sync + 'static, +// { +// #[cfg_attr(track_caller, track_caller)] +// fn from(error: E) -> Self { +// Report::from_std(error) +// } +// } + impl Deref for Report { type Target = dyn StdError + Send + Sync + 'static; diff --git a/eyre/tests/test_anyhow.rs b/eyre/tests/test_anyhow.rs new file mode 100644 index 0000000..53145e3 --- /dev/null +++ b/eyre/tests/test_anyhow.rs @@ -0,0 +1,55 @@ +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 bubble() -> eyre::Result<()> { + use anyhow::Context; + this_function_fails().context("Anyhow context B")?; + + Ok(()) +} + +#[test] +fn anyhow_conversion() { + use eyre::WrapErr; + let error: Report = bubble().wrap_err("Eyre context").unwrap_err(); + + eprintln!("Error: {:?}", error); + + let chain = error.chain().map(ToString::to_string).collect::>(); + 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 error = Report::msg("A").wrap_err("B").wrap_err("C"); +}