feat: basic backtrace propagation

This commit is contained in:
Freja Roberts 2023-11-29 12:54:20 +01:00
parent 29aec58099
commit b52dba1323
3 changed files with 69 additions and 25 deletions

View File

@ -1,8 +1,9 @@
#[cfg(backtrace)] #[cfg(backtrace)]
pub(crate) use std::backtrace::Backtrace; pub use std::backtrace::Backtrace;
#[cfg(not(backtrace))] #[cfg(not(backtrace))]
pub(crate) enum Backtrace {} #[derive(Debug)]
pub enum Backtrace {}
#[cfg(backtrace)] #[cfg(backtrace)]
macro_rules! backtrace_if_absent { macro_rules! backtrace_if_absent {

View File

@ -1,4 +1,4 @@
use crate::Report; use crate::{builder::ReportBuilder, Report};
/// Convert this result into an eyre [`Report`](crate::Report) result /// Convert this result into an eyre [`Report`](crate::Report) result
/// ///
@ -6,12 +6,7 @@ use crate::Report;
/// [`Error`](std::error::Error) is not yet available. /// [`Error`](std::error::Error) is not yet available.
pub trait IntoEyre<T> { pub trait IntoEyre<T> {
/// Convert this result into an eyre [`Report`](crate::Report) result /// Convert this result into an eyre [`Report`](crate::Report) result
fn into_eyre(self) -> crate::Result<T> fn into_eyre(self) -> crate::Result<T>;
where
Self: Sized,
{
todo!()
}
} }
/// See: [`IntoEyre`] /// See: [`IntoEyre`]
@ -19,25 +14,27 @@ pub trait IntoEyre<T> {
/// Error type automatically implements `into_eyre` for `Result<T, E>` /// Error type automatically implements `into_eyre` for `Result<T, E>`
pub trait IntoEyreReport { pub trait IntoEyreReport {
/// Convert this error into an eyre [`Report`](crate::Report) /// Convert this error into an eyre [`Report`](crate::Report)
fn into_eyre_report(self) -> Report #[track_caller]
where fn into_eyre_report(self) -> Report;
Self: Sized,
{
todo!()
}
} }
impl<T, E> IntoEyre<T> for Result<T, E> impl<T, E> IntoEyre<T> for Result<T, E>
where where
E: IntoEyreReport, E: IntoEyreReport,
{ {
#[track_caller]
fn into_eyre(self) -> crate::Result<T> { fn into_eyre(self) -> crate::Result<T> {
self.map_err(E::into_eyre_report) // Use a manual match to keep backtrace
match self {
Ok(v) => Ok(v),
Err(err) => Err(err.into_eyre_report()),
}
} }
} }
#[cfg(feature = "anyhow-compat")] #[cfg(feature = "anyhow-compat")]
impl IntoEyreReport for anyhow::Error { impl IntoEyreReport for anyhow::Error {
#[track_caller]
fn into_eyre_report(self) -> Report fn into_eyre_report(self) -> Report
where where
Self: Sized, Self: Sized,
@ -60,7 +57,13 @@ impl IntoEyreReport for anyhow::Error {
.next() .next()
.expect("Error chain contains at least one error"); .expect("Error chain contains at least one error");
let report = Report::msg(head.to_string()); #[cfg(backtrace)]
let report = ReportBuilder::default()
.with_backtrace(self.backtrace())
.msg(head.to_string());
#[cfg(not(backtrace))]
let report = ReportBuilder::default().msg(head.to_string());
// chai // chai
// eprintln!("{:?}", chain.map(|v| v.to_string()).collect::<Vec<_>>()); // eprintln!("{:?}", chain.map(|v| v.to_string()).collect::<Vec<_>>());

View File

@ -1,24 +1,64 @@
use anyhow::Context; use std::fmt::Display;
use eyre::WrapErr;
use eyre::{compat::IntoEyre, Report}; use eyre::{compat::IntoEyre, Report};
fn this_function_fails() -> anyhow::Result<()> { #[derive(Debug)]
anyhow::bail!("Ouch!") 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
}
}
#[cfg(feature = "anyhow-compat")]
fn this_function_fails() -> anyhow::Result<()> {
use anyhow::Context;
Err(RootError).context("Ouch!").context("Anyhow context A")
}
#[cfg(feature = "anyhow-compat")]
fn bubble() -> eyre::Result<()> { fn bubble() -> eyre::Result<()> {
use anyhow::Context;
use eyre::WrapErr;
this_function_fails() this_function_fails()
.context("Anyhow::context") .context("Anyhow context B")
.into_eyre() .into_eyre()
.wrap_err("Eyre::wrap_err")?; .wrap_err("Eyre context A")?;
Ok(()) Ok(())
} }
#[cfg(feature = "anyhow-compat")]
#[test] #[test]
fn anyhow_conversion() { fn anyhow_conversion() {
let error: Report = bubble().unwrap_err(); use eyre::WrapErr;
// let error = Report::msg("A").wrap_err("B").wrap_err("C"); let error: Report = bubble().wrap_err("Eyre context B").unwrap_err();
eprintln!("{error:?}"); eprintln!("{error:?}");
let chain = error.chain().map(ToString::to_string).collect::<Vec<_>>();
assert_eq!(
chain,
[
"Eyre context B",
"Eyre context A",
// 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");
} }