use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
#[cfg(feature = "clean-backtrace")]
use backtrace::Backtrace;
#[cfg(not(feature = "clean-backtrace"))]
use std::backtrace::Backtrace;
pub type Result<T, E> = std::result::Result<T, Backtraced<E>>;
pub struct Backtraced<E> {
pub inner: E,
backtrace: Backtrace,
}
impl<E> Debug for Backtraced<E>
where
E: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
writeln!(
f,
"{:?}\n\nBacktrace:\n{}",
&self.inner,
format_backtrace(&self.backtrace)
)
} else {
writeln!(f, "{:?}", &self.inner)
}
}
}
impl<E> From<E> for Backtraced<E> {
fn from(value: E) -> Self {
Backtraced {
inner: value,
#[cfg(feature = "clean-backtrace")]
backtrace: Backtrace::new(),
#[cfg(not(feature = "clean-backtrace"))]
backtrace: Backtrace::capture(),
}
}
}
pub trait ResultBacktrace {
fn backtrace(self) -> Self;
}
impl<T, E> ResultBacktrace for std::result::Result<T, Backtraced<E>> {
fn backtrace(self) -> Self {
#[cfg(all(feature = "debug-only", not(debug_assertions)))]
return self;
let Err(ref error) = self else {
return self;
};
println!("Error backtrace:\n{}", format_backtrace(&error.backtrace));
self
}
}
#[cfg(not(feature = "clean-backtrace"))]
fn format_backtrace(backtrace: &Backtrace) -> String {
format!("{backtrace:?}")
}
#[cfg(feature = "clean-backtrace")]
fn format_backtrace(backtrace: &Backtrace) -> String {
let frames = &backtrace.frames()[1..];
let bt = Backtrace::new();
let current_frames: Vec<_> = bt.frames().iter().map(|f| f.ip()).collect();
let frames: Vec<_> = frames
.iter()
.filter(|f| {
f.symbols()
.get(0)
.map(|symbol| {
symbol
.name()
.map(|name| {
let formated_name = format!("{:#?}", name);
&formated_name != "<T as core::convert::Into<U>>::into"
})
.unwrap_or(true)
})
.unwrap_or(true)
})
.filter(|x| !current_frames.contains(&x.ip()))
.cloned()
.collect();
let backtrace: Backtrace = frames.to_vec().into();
format!("{backtrace:?}")
}
impl<E> Deref for Backtraced<E> {
type Target = E;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<E> DerefMut for Backtraced<E> {
fn deref_mut(&mut self) -> &mut E {
&mut self.inner
}
}
impl<E: PartialEq> PartialEq for Backtraced<E> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}