color_eyre/
handler.rs

1use crate::{
2    config::BacktraceFormatter,
3    section::help::HelpInfo,
4    writers::{EnvSection, WriterExt},
5    Handler,
6};
7use backtrace::Backtrace;
8use indenter::{indented, Format};
9use std::fmt::Write;
10#[cfg(feature = "capture-spantrace")]
11use tracing_error::{ExtractSpanTrace, SpanTrace};
12
13impl std::fmt::Debug for Handler {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        f.write_str("redacted")
16    }
17}
18
19impl Handler {
20    /// Return a reference to the captured `Backtrace` type
21    pub fn backtrace(&self) -> Option<&Backtrace> {
22        self.backtrace.as_ref()
23    }
24
25    /// Return a reference to the captured `SpanTrace` type
26    #[cfg(feature = "capture-spantrace")]
27    #[cfg_attr(docsrs, doc(cfg(feature = "capture-spantrace")))]
28    pub fn span_trace(&self) -> Option<&SpanTrace> {
29        self.span_trace.as_ref()
30    }
31
32    pub(crate) fn format_backtrace<'a>(
33        &'a self,
34        trace: &'a backtrace::Backtrace,
35    ) -> BacktraceFormatter<'a> {
36        BacktraceFormatter {
37            filters: &self.filters,
38            inner: trace,
39            theme: self.theme,
40        }
41    }
42}
43
44impl eyre::EyreHandler for Handler {
45    fn debug(
46        &self,
47        error: &(dyn std::error::Error + 'static),
48        f: &mut core::fmt::Formatter<'_>,
49    ) -> core::fmt::Result {
50        if f.alternate() {
51            return core::fmt::Debug::fmt(error, f);
52        }
53
54        #[cfg(feature = "capture-spantrace")]
55        let errors = || {
56            eyre::Chain::new(error)
57                .filter(|e| e.span_trace().is_none())
58                .enumerate()
59        };
60
61        #[cfg(not(feature = "capture-spantrace"))]
62        let errors = || eyre::Chain::new(error).enumerate();
63
64        for (n, error) in errors() {
65            writeln!(f)?;
66            write!(indented(f).ind(n), "{}", self.theme.error.style(error))?;
67        }
68
69        let mut separated = f.header("\n\n");
70
71        #[cfg(feature = "track-caller")]
72        if self.display_location_section {
73            write!(
74                separated.ready(),
75                "{}",
76                crate::SectionExt::header(
77                    crate::fmt::LocationSection(self.location, self.theme),
78                    "Location:"
79                )
80            )?;
81        }
82
83        for section in self
84            .sections
85            .iter()
86            .filter(|s| matches!(s, HelpInfo::Error(_, _)))
87        {
88            write!(separated.ready(), "{}", section)?;
89        }
90
91        for section in self
92            .sections
93            .iter()
94            .filter(|s| matches!(s, HelpInfo::Custom(_)))
95        {
96            write!(separated.ready(), "{}", section)?;
97        }
98
99        #[cfg(feature = "capture-spantrace")]
100        let span_trace = self
101            .span_trace
102            .as_ref()
103            .or_else(|| get_deepest_spantrace(error));
104
105        #[cfg(feature = "capture-spantrace")]
106        {
107            if let Some(span_trace) = span_trace {
108                write!(
109                    &mut separated.ready(),
110                    "{}",
111                    crate::writers::FormattedSpanTrace(span_trace)
112                )?;
113            }
114        }
115
116        if !self.suppress_backtrace {
117            if let Some(backtrace) = self.backtrace.as_ref() {
118                let fmted_bt = self.format_backtrace(backtrace);
119
120                write!(
121                    indented(&mut separated.ready())
122                        .with_format(Format::Uniform { indentation: "  " }),
123                    "{}",
124                    fmted_bt
125                )?;
126            }
127        }
128
129        let f = separated.ready();
130        let mut h = f.header("\n");
131        let mut f = h.in_progress();
132
133        for section in self
134            .sections
135            .iter()
136            .filter(|s| !matches!(s, HelpInfo::Custom(_) | HelpInfo::Error(_, _)))
137        {
138            write!(&mut f, "{}", section)?;
139            f = h.ready();
140        }
141
142        if self.display_env_section {
143            let env_section = EnvSection {
144                bt_captured: &self.backtrace.is_some(),
145                #[cfg(feature = "capture-spantrace")]
146                span_trace,
147            };
148
149            write!(&mut separated.ready(), "{}", env_section)?;
150        }
151
152        #[cfg(feature = "issue-url")]
153        if self.issue_url.is_some() && (*self.issue_filter)(crate::ErrorKind::Recoverable(error)) {
154            let url = self.issue_url.as_ref().unwrap();
155            let mut payload = String::from("Error: ");
156            for (n, error) in errors() {
157                writeln!(&mut payload)?;
158                write!(indented(&mut payload).ind(n), "{}", error)?;
159            }
160
161            let issue_section = crate::section::github::IssueSection::new(url, &payload)
162                .with_backtrace(self.backtrace.as_ref())
163                .with_metadata(&self.issue_metadata);
164
165            #[cfg(feature = "capture-spantrace")]
166            let issue_section = issue_section.with_span_trace(span_trace);
167
168            write!(&mut separated.ready(), "{}", issue_section)?;
169        }
170
171        Ok(())
172    }
173
174    #[cfg(feature = "track-caller")]
175    fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {
176        self.location = Some(location);
177    }
178}
179
180#[cfg(feature = "capture-spantrace")]
181pub(crate) fn get_deepest_spantrace<'a>(
182    error: &'a (dyn std::error::Error + 'static),
183) -> Option<&'a SpanTrace> {
184    eyre::Chain::new(error)
185        .rev()
186        .flat_map(|error| error.span_trace())
187        .next()
188}