error-backtrace 0.3.1

Simple crate to backtrace your errors
Documentation
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()
        // Skip .into()
        .filter(|f| {
            f.symbols()
                .get(0)
                .map(|symbol| {
                    symbol
                        .name()
                        .map(|name| {
                            let formated_name = format!("{:#?}", name);
                            // Not sure if this is the case for all systems
                            &formated_name != "<T as core::convert::Into<U>>::into"
                        })
                        .unwrap_or(true)
                })
                .unwrap_or(true)
        })
        // This skips everything before backtrace was called, including everything before main.
        .filter(|x| !current_frames.contains(&x.ip()))
        .cloned()
        .collect();

    let backtrace: Backtrace = frames.to_vec().into();

    format!("{backtrace:?}")
}

// Extra

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
    }
}