error_backtrace/
lib.rs

1use std::{
2    fmt::Debug,
3    ops::{Deref, DerefMut},
4};
5
6#[cfg(feature = "clean-backtrace")]
7use backtrace::Backtrace;
8#[cfg(not(feature = "clean-backtrace"))]
9use std::backtrace::Backtrace;
10
11pub type Result<T, E> = std::result::Result<T, Backtraced<E>>;
12
13pub struct Backtraced<E> {
14    pub inner: E,
15    backtrace: Backtrace,
16}
17
18impl<E> Debug for Backtraced<E>
19where
20    E: Debug,
21{
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        if f.alternate() {
24            writeln!(
25                f,
26                "{:?}\n\nBacktrace:\n{}",
27                &self.inner,
28                format_backtrace(&self.backtrace)
29            )
30        } else {
31            writeln!(f, "{:?}", &self.inner)
32        }
33    }
34}
35
36impl<E> From<E> for Backtraced<E> {
37    fn from(value: E) -> Self {
38        Backtraced {
39            inner: value,
40            #[cfg(feature = "clean-backtrace")]
41            backtrace: Backtrace::new(),
42            #[cfg(not(feature = "clean-backtrace"))]
43            backtrace: Backtrace::capture(),
44        }
45    }
46}
47
48pub trait ResultBacktrace {
49    fn backtrace(self) -> Self;
50}
51
52impl<T, E> ResultBacktrace for std::result::Result<T, Backtraced<E>> {
53    fn backtrace(self) -> Self {
54        #[cfg(all(feature = "debug-only", not(debug_assertions)))]
55        return self;
56
57        let Err(ref error) = self else {
58            return self;
59        };
60
61        println!("Error backtrace:\n{}", format_backtrace(&error.backtrace));
62
63        self
64    }
65}
66
67#[cfg(not(feature = "clean-backtrace"))]
68fn format_backtrace(backtrace: &Backtrace) -> String {
69    format!("{backtrace:?}")
70}
71
72#[cfg(feature = "clean-backtrace")]
73fn format_backtrace(backtrace: &Backtrace) -> String {
74    let frames = &backtrace.frames()[1..];
75    let bt = Backtrace::new();
76    let current_frames: Vec<_> = bt.frames().iter().map(|f| f.ip()).collect();
77
78    let frames: Vec<_> = frames
79        .iter()
80        // Skip .into()
81        .filter(|f| {
82            f.symbols()
83                .get(0)
84                .map(|symbol| {
85                    symbol
86                        .name()
87                        .map(|name| {
88                            let formated_name = format!("{:#?}", name);
89                            // Not sure if this is the case for all systems
90                            &formated_name != "<T as core::convert::Into<U>>::into"
91                        })
92                        .unwrap_or(true)
93                })
94                .unwrap_or(true)
95        })
96        // This skips everything before backtrace was called, including everything before main.
97        .filter(|x| !current_frames.contains(&x.ip()))
98        .cloned()
99        .collect();
100
101    let backtrace: Backtrace = frames.to_vec().into();
102
103    format!("{backtrace:?}")
104}
105
106// Extra
107
108impl<E> Deref for Backtraced<E> {
109    type Target = E;
110
111    fn deref(&self) -> &Self::Target {
112        &self.inner
113    }
114}
115
116impl<E> DerefMut for Backtraced<E> {
117    fn deref_mut(&mut self) -> &mut E {
118        &mut self.inner
119    }
120}
121
122impl<E: PartialEq> PartialEq for Backtraced<E> {
123    fn eq(&self, other: &Self) -> bool {
124        self.inner == other.inner
125    }
126}