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<&mut Formatter<'_>> = 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: Verbosity = 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<'_, &str, &mut …> = 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: &SpanTrace) = 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}
262