color_eyre/
writers.rs

1use crate::config::{lib_verbosity, panic_verbosity, Verbosity};
2use fmt::Write;
3use std::fmt::{self, Display};
4#[cfg(feature = "capture-spantrace")]
5use tracing_error::{SpanTrace, SpanTraceStatus};
6
7#[allow(explicit_outlives_requirements)]
8pub(crate) struct HeaderWriter<'a, H, W>
9where
10    H: ?Sized,
11{
12    inner: W,
13    header: &'a H,
14    started: bool,
15}
16
17pub(crate) trait WriterExt: Sized {
18    fn header<H: ?Sized>(self, header: &H) -> HeaderWriter<'_, H, Self>;
19}
20
21impl<W> WriterExt for W {
22    fn header<H: ?Sized>(self, header: &H) -> HeaderWriter<'_, H, Self> {
23        HeaderWriter {
24            inner: self,
25            header,
26            started: false,
27        }
28    }
29}
30
31pub(crate) trait DisplayExt: Sized + Display {
32    fn with_header<H: Display>(self, header: H) -> Header<Self, H>;
33    fn with_footer<F: Display>(self, footer: F) -> Footer<Self, F>;
34}
35
36impl<T> DisplayExt for T
37where
38    T: Display,
39{
40    fn with_footer<F: Display>(self, footer: F) -> Footer<Self, F> {
41        Footer { body: self, footer }
42    }
43
44    fn with_header<H: Display>(self, header: H) -> Header<Self, H> {
45        Header {
46            body: self,
47            h: header,
48        }
49    }
50}
51
52pub(crate) struct ReadyHeaderWriter<'a, 'b, H: ?Sized, W>(&'b mut HeaderWriter<'a, H, W>);
53
54impl<'a, H: ?Sized, W> HeaderWriter<'a, H, W> {
55    pub(crate) fn ready(&mut self) -> ReadyHeaderWriter<'a, '_, H, W> {
56        self.started = false;
57
58        ReadyHeaderWriter(self)
59    }
60
61    pub(crate) fn in_progress(&mut self) -> ReadyHeaderWriter<'a, '_, H, W> {
62        self.started = true;
63
64        ReadyHeaderWriter(self)
65    }
66}
67
68impl<'a, H: ?Sized, W> fmt::Write for ReadyHeaderWriter<'a, '_, H, W>
69where
70    H: Display,
71    W: fmt::Write,
72{
73    fn write_str(&mut self, s: &str) -> fmt::Result {
74        if !self.0.started && !s.is_empty() {
75            self.0.inner.write_fmt(format_args!("{}", self.0.header))?;
76            self.0.started = true;
77        }
78
79        self.0.inner.write_str(s)
80    }
81}
82
83pub(crate) struct FooterWriter<W> {
84    inner: W,
85    had_output: bool,
86}
87
88impl<W> fmt::Write for FooterWriter<W>
89where
90    W: fmt::Write,
91{
92    fn write_str(&mut self, s: &str) -> fmt::Result {
93        if !self.had_output && !s.is_empty() {
94            self.had_output = true;
95        }
96
97        self.inner.write_str(s)
98    }
99}
100
101#[allow(explicit_outlives_requirements)]
102pub(crate) struct Footer<B, H>
103where
104    B: Display,
105    H: Display,
106{
107    body: B,
108    footer: H,
109}
110
111impl<B, H> fmt::Display for Footer<B, H>
112where
113    B: Display,
114    H: Display,
115{
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        let mut inner_f = FooterWriter {
118            inner: &mut *f,
119            had_output: false,
120        };
121
122        write!(&mut inner_f, "{}", self.body)?;
123
124        if inner_f.had_output {
125            self.footer.fmt(f)?;
126        }
127
128        Ok(())
129    }
130}
131
132#[allow(explicit_outlives_requirements)]
133pub(crate) struct Header<B, H>
134where
135    B: Display,
136    H: Display,
137{
138    body: B,
139    h: H,
140}
141
142impl<B, H> fmt::Display for Header<B, H>
143where
144    B: Display,
145    H: Display,
146{
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f.header(&self.h).ready(), "{}", self.body)?;
149
150        Ok(())
151    }
152}
153
154#[cfg(feature = "capture-spantrace")]
155pub(crate) struct FormattedSpanTrace<'a>(pub(crate) &'a SpanTrace);
156
157#[cfg(feature = "capture-spantrace")]
158impl fmt::Display for FormattedSpanTrace<'_> {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        use indenter::indented;
161        use indenter::Format;
162
163        if self.0.status() == SpanTraceStatus::CAPTURED {
164            write!(
165                indented(f).with_format(Format::Uniform { indentation: "  " }),
166                "{}",
167                color_spantrace::colorize(self.0)
168            )?;
169        }
170
171        Ok(())
172    }
173}
174
175pub(crate) struct EnvSection<'a> {
176    pub(crate) bt_captured: &'a bool,
177    #[cfg(feature = "capture-spantrace")]
178    pub(crate) span_trace: Option<&'a SpanTrace>,
179}
180
181impl fmt::Display for EnvSection<'_> {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        let v = if std::thread::panicking() {
184            panic_verbosity()
185        } else {
186            lib_verbosity()
187        };
188        write!(f, "{}", BacktraceOmited(!self.bt_captured))?;
189
190        let mut separated = HeaderWriter {
191            inner: &mut *f,
192            header: &"\n",
193            started: false,
194        };
195        write!(&mut separated.ready(), "{}", SourceSnippets(v))?;
196        #[cfg(feature = "capture-spantrace")]
197        write!(
198            &mut separated.ready(),
199            "{}",
200            SpanTraceOmited(self.span_trace)
201        )?;
202        Ok(())
203    }
204}
205
206#[cfg(feature = "capture-spantrace")]
207struct SpanTraceOmited<'a>(Option<&'a SpanTrace>);
208
209#[cfg(feature = "capture-spantrace")]
210impl fmt::Display for SpanTraceOmited<'_> {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        if let Some(span_trace) = self.0 {
213            if span_trace.status() == SpanTraceStatus::UNSUPPORTED {
214                writeln!(f, "Warning: SpanTrace capture is Unsupported.")?;
215                write!(
216                    f,
217                    "Ensure that you've setup a tracing-error ErrorLayer and the semver versions are compatible"
218                )?;
219            }
220        }
221
222        Ok(())
223    }
224}
225
226struct BacktraceOmited(bool);
227
228impl fmt::Display for BacktraceOmited {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        // Print some info on how to increase verbosity.
231        if self.0 {
232            write!(
233                f,
234                "Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it."
235            )?;
236        } else {
237            // This text only makes sense if frames are displayed.
238            write!(
239                f,
240                "Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering."
241            )?;
242        }
243
244        Ok(())
245    }
246}
247
248struct SourceSnippets(Verbosity);
249
250impl fmt::Display for SourceSnippets {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        if self.0 <= Verbosity::Medium {
253            write!(
254                f,
255                "Run with RUST_BACKTRACE=full to include source snippets."
256            )?;
257        }
258
259        Ok(())
260    }
261}