1 | use crate::config::{lib_verbosity, panic_verbosity, Verbosity}; |
2 | use fmt::Write; |
3 | use std::fmt::{self, Display}; |
4 | #[cfg (feature = "capture-spantrace" )] |
5 | use tracing_error::{SpanTrace, SpanTraceStatus}; |
6 | |
7 | #[allow (explicit_outlives_requirements)] |
8 | pub(crate) struct HeaderWriter<'a, H, W> |
9 | where |
10 | H: ?Sized, |
11 | { |
12 | inner: W, |
13 | header: &'a H, |
14 | started: bool, |
15 | } |
16 | |
17 | pub(crate) trait WriterExt: Sized { |
18 | fn header<H: ?Sized>(self, header: &H) -> HeaderWriter<'_, H, Self>; |
19 | } |
20 | |
21 | impl<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 | |
31 | pub(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 | |
36 | impl<T> DisplayExt for T |
37 | where |
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 | |
52 | pub(crate) struct ReadyHeaderWriter<'a, 'b, H: ?Sized, W>(&'b mut HeaderWriter<'a, H, W>); |
53 | |
54 | impl<'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 | |
68 | impl<'a, H: ?Sized, W> fmt::Write for ReadyHeaderWriter<'a, '_, H, W> |
69 | where |
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 | |
83 | pub(crate) struct FooterWriter<W> { |
84 | inner: W, |
85 | had_output: bool, |
86 | } |
87 | |
88 | impl<W> fmt::Write for FooterWriter<W> |
89 | where |
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)] |
102 | pub(crate) struct Footer<B, H> |
103 | where |
104 | B: Display, |
105 | H: Display, |
106 | { |
107 | body: B, |
108 | footer: H, |
109 | } |
110 | |
111 | impl<B, H> fmt::Display for Footer<B, H> |
112 | where |
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)] |
133 | pub(crate) struct Header<B, H> |
134 | where |
135 | B: Display, |
136 | H: Display, |
137 | { |
138 | body: B, |
139 | h: H, |
140 | } |
141 | |
142 | impl<B, H> fmt::Display for Header<B, H> |
143 | where |
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" )] |
155 | pub(crate) struct FormattedSpanTrace<'a>(pub(crate) &'a SpanTrace); |
156 | |
157 | #[cfg (feature = "capture-spantrace" )] |
158 | impl 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 | |
175 | pub(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 | |
181 | impl 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" )] |
207 | struct SpanTraceOmited<'a>(Option<&'a SpanTrace>); |
208 | |
209 | #[cfg (feature = "capture-spantrace" )] |
210 | impl 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 | |
226 | struct BacktraceOmited(bool); |
227 | |
228 | impl 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 | |
248 | struct SourceSnippets(Verbosity); |
249 | |
250 | impl 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 | |