1use super::*;
2use crate::{
3 field::{VisitFmt, VisitOutput},
4 fmt::fmt_layer::{FmtContext, FormattedFields},
5 registry::LookupSpan,
6};
7
8use std::fmt;
9use tracing_core::{
10 field::{self, Field},
11 Event, Level, Subscriber,
12};
13
14#[cfg(feature = "tracing-log")]
15use tracing_log::NormalizeEvent;
16
17use nu_ansi_term::{Color, Style};
18
19/// An excessively pretty, human-readable event formatter.
20///
21/// Unlike the [`Full`], [`Compact`], and [`Json`] formatters, this is a
22/// multi-line output format. Each individual event may output multiple lines of
23/// text.
24///
25/// # Example Output
26///
27/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-pretty
28/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
29/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-pretty`
30/// 2022-02-15T18:44:24.535324Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: preparing to shave yaks, </font><font color="#4E9A06"><b>number_of_yaks</b></font><font color="#4E9A06">: 3</font>
31/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:16 <font color="#AAAAAA"><i>on</i></font> main
32///
33/// 2022-02-15T18:44:24.535403Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty::yak_shave</b></font><font color="#4E9A06">: shaving yaks</font>
34/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:41 <font color="#AAAAAA"><i>on</i></font> main
35/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
36///
37/// 2022-02-15T18:44:24.535442Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
38/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
39/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
40/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
41///
42/// 2022-02-15T18:44:24.535469Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
43/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
44/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
45/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
46///
47/// 2022-02-15T18:44:24.535502Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 1, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
48/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
49/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
50///
51/// 2022-02-15T18:44:24.535524Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 1</font>
52/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
53/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
54///
55/// 2022-02-15T18:44:24.535551Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
56/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
57/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
58/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
59///
60/// 2022-02-15T18:44:24.535573Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
61/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
62/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
63/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
64///
65/// 2022-02-15T18:44:24.535600Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 2, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
66/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
67/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
68///
69/// 2022-02-15T18:44:24.535618Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
70/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
71/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
72///
73/// 2022-02-15T18:44:24.535644Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
74/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
75/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
76/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
77///
78/// 2022-02-15T18:44:24.535670Z <font color="#C4A000"> WARN</font> <font color="#C4A000"><b>fmt_pretty::yak_shave</b></font><font color="#C4A000">: could not locate yak</font>
79/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:18 <font color="#AAAAAA"><i>on</i></font> main
80/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
81/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
82///
83/// 2022-02-15T18:44:24.535698Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 3, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: false</font>
84/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
85/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
86///
87/// 2022-02-15T18:44:24.535720Z <font color="#CC0000">ERROR</font> <font color="#CC0000"><b>fmt_pretty::yak_shave</b></font><font color="#CC0000">: failed to shave yak, </font><font color="#CC0000"><b>yak</b></font><font color="#CC0000">: 3, </font><font color="#CC0000"><b>error</b></font><font color="#CC0000">: missing yak, </font><font color="#CC0000"><b>error.sources</b></font><font color="#CC0000">: [out of space, out of cash]</font>
88/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:51 <font color="#AAAAAA"><i>on</i></font> main
89/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
90///
91/// 2022-02-15T18:44:24.535742Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
92/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
93/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
94///
95/// 2022-02-15T18:44:24.535765Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: yak shaving completed, </font><font color="#4E9A06"><b>all_yaks_shaved</b></font><font color="#4E9A06">: false</font>
96/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:19 <font color="#AAAAAA"><i>on</i></font> main
97/// </pre>
98#[derive(Debug, Clone, Eq, PartialEq)]
99pub struct Pretty {
100 display_location: bool,
101}
102
103/// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation.
104///
105/// [visitor]: field::Visit
106/// [`MakeVisitor`]: crate::field::MakeVisitor
107#[derive(Debug)]
108pub struct PrettyVisitor<'a> {
109 writer: Writer<'a>,
110 is_empty: bool,
111 style: Style,
112 result: fmt::Result,
113}
114
115/// An excessively pretty, human-readable [`MakeVisitor`] implementation.
116///
117/// [`MakeVisitor`]: crate::field::MakeVisitor
118#[derive(Debug)]
119pub struct PrettyFields {
120 /// A value to override the provided `Writer`'s ANSI formatting
121 /// configuration.
122 ///
123 /// If this is `Some`, we override the `Writer`'s ANSI setting. This is
124 /// necessary in order to continue supporting the deprecated
125 /// `PrettyFields::with_ansi` method. If it is `None`, we don't override the
126 /// ANSI formatting configuration (because the deprecated method was not
127 /// called).
128 // TODO: when `PrettyFields::with_ansi` is removed, we can get rid
129 // of this entirely.
130 ansi: Option<bool>,
131}
132
133// === impl Pretty ===
134
135impl Default for Pretty {
136 fn default() -> Self {
137 Self {
138 display_location: true,
139 }
140 }
141}
142
143impl Pretty {
144 fn style_for(level: &Level) -> Style {
145 match *level {
146 Level::TRACE => Style::new().fg(Color::Purple),
147 Level::DEBUG => Style::new().fg(Color::Blue),
148 Level::INFO => Style::new().fg(Color::Green),
149 Level::WARN => Style::new().fg(Color::Yellow),
150 Level::ERROR => Style::new().fg(Color::Red),
151 }
152 }
153
154 /// Sets whether the event's source code location is displayed.
155 ///
156 /// This defaults to `true`.
157 #[deprecated(
158 since = "0.3.6",
159 note = "all formatters now support configurable source locations. Use `Format::with_source_location` instead."
160 )]
161 pub fn with_source_location(self, display_location: bool) -> Self {
162 Self {
163 display_location,
164 ..self
165 }
166 }
167}
168
169impl<C, N, T> FormatEvent<C, N> for Format<Pretty, T>
170where
171 C: Subscriber + for<'a> LookupSpan<'a>,
172 N: for<'a> FormatFields<'a> + 'static,
173 T: FormatTime,
174{
175 fn format_event(
176 &self,
177 ctx: &FmtContext<'_, C, N>,
178 mut writer: Writer<'_>,
179 event: &Event<'_>,
180 ) -> fmt::Result {
181 #[cfg(feature = "tracing-log")]
182 let normalized_meta = event.normalized_metadata();
183 #[cfg(feature = "tracing-log")]
184 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
185 #[cfg(not(feature = "tracing-log"))]
186 let meta = event.metadata();
187 write!(&mut writer, " ")?;
188
189 // if the `Format` struct *also* has an ANSI color configuration,
190 // override the writer...the API for configuring ANSI color codes on the
191 // `Format` struct is deprecated, but we still need to honor those
192 // configurations.
193 if let Some(ansi) = self.ansi {
194 writer = writer.with_ansi(ansi);
195 }
196
197 self.format_timestamp(&mut writer)?;
198
199 let style = if self.display_level && writer.has_ansi_escapes() {
200 Pretty::style_for(meta.level())
201 } else {
202 Style::new()
203 };
204
205 if self.display_level {
206 write!(
207 writer,
208 "{} ",
209 super::FmtLevel::new(meta.level(), writer.has_ansi_escapes())
210 )?;
211 }
212
213 if self.display_target {
214 let target_style = if writer.has_ansi_escapes() {
215 style.bold()
216 } else {
217 style
218 };
219 write!(
220 writer,
221 "{}{}{}:",
222 target_style.prefix(),
223 meta.target(),
224 target_style.infix(style)
225 )?;
226 }
227 let line_number = if self.display_line_number {
228 meta.line()
229 } else {
230 None
231 };
232
233 // If the file name is disabled, format the line number right after the
234 // target. Otherwise, if we also display the file, it'll go on a
235 // separate line.
236 if let (Some(line_number), false, true) = (
237 line_number,
238 self.display_filename,
239 self.format.display_location,
240 ) {
241 write!(
242 writer,
243 "{}{}{}:",
244 style.prefix(),
245 line_number,
246 style.infix(style)
247 )?;
248 }
249
250 writer.write_char(' ')?;
251
252 let mut v = PrettyVisitor::new(writer.by_ref(), true).with_style(style);
253 event.record(&mut v);
254 v.finish()?;
255 writer.write_char('\n')?;
256
257 let dimmed = if writer.has_ansi_escapes() {
258 Style::new().dimmed().italic()
259 } else {
260 Style::new()
261 };
262 let thread = self.display_thread_name || self.display_thread_id;
263
264 if let (Some(file), true, true) = (
265 meta.file(),
266 self.format.display_location,
267 self.display_filename,
268 ) {
269 write!(writer, " {} {}", dimmed.paint("at"), file,)?;
270
271 if let Some(line) = line_number {
272 write!(writer, ":{}", line)?;
273 }
274 writer.write_char(if thread { ' ' } else { '\n' })?;
275 } else if thread {
276 write!(writer, " ")?;
277 };
278
279 if thread {
280 write!(writer, "{} ", dimmed.paint("on"))?;
281 let thread = std::thread::current();
282 if self.display_thread_name {
283 if let Some(name) = thread.name() {
284 write!(writer, "{}", name)?;
285 if self.display_thread_id {
286 writer.write_char(' ')?;
287 }
288 }
289 }
290 if self.display_thread_id {
291 write!(writer, "{:?}", thread.id())?;
292 }
293 writer.write_char('\n')?;
294 }
295
296 let bold = writer.bold();
297 let span = event
298 .parent()
299 .and_then(|id| ctx.span(id))
300 .or_else(|| ctx.lookup_current());
301
302 let scope = span.into_iter().flat_map(|span| span.scope());
303
304 for span in scope {
305 let meta = span.metadata();
306 if self.display_target {
307 write!(
308 writer,
309 " {} {}::{}",
310 dimmed.paint("in"),
311 meta.target(),
312 bold.paint(meta.name()),
313 )?;
314 } else {
315 write!(
316 writer,
317 " {} {}",
318 dimmed.paint("in"),
319 bold.paint(meta.name()),
320 )?;
321 }
322
323 let ext = span.extensions();
324 let fields = &ext
325 .get::<FormattedFields<N>>()
326 .expect("Unable to find FormattedFields in extensions; this is a bug");
327 if !fields.is_empty() {
328 write!(writer, " {} {}", dimmed.paint("with"), fields)?;
329 }
330 writer.write_char('\n')?;
331 }
332
333 writer.write_char('\n')
334 }
335}
336
337impl<'writer> FormatFields<'writer> for Pretty {
338 fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result {
339 let mut v: PrettyVisitor<'_> = PrettyVisitor::new(writer, is_empty:true);
340 fields.record(&mut v);
341 v.finish()
342 }
343
344 fn add_fields(
345 &self,
346 current: &'writer mut FormattedFields<Self>,
347 fields: &span::Record<'_>,
348 ) -> fmt::Result {
349 let empty: bool = current.is_empty();
350 let writer: Writer<'_> = current.as_writer();
351 let mut v: PrettyVisitor<'_> = PrettyVisitor::new(writer, is_empty:empty);
352 fields.record(&mut v);
353 v.finish()
354 }
355}
356
357// === impl PrettyFields ===
358
359impl Default for PrettyFields {
360 fn default() -> Self {
361 Self::new()
362 }
363}
364
365impl PrettyFields {
366 /// Returns a new default [`PrettyFields`] implementation.
367 pub fn new() -> Self {
368 // By default, don't override the `Writer`'s ANSI colors
369 // configuration. We'll only do this if the user calls the
370 // deprecated `PrettyFields::with_ansi` method.
371 Self { ansi: None }
372 }
373
374 /// Enable ANSI encoding for formatted fields.
375 #[deprecated(
376 since = "0.3.3",
377 note = "Use `fmt::Subscriber::with_ansi` or `fmt::Layer::with_ansi` instead."
378 )]
379 pub fn with_ansi(self, ansi: bool) -> Self {
380 Self {
381 ansi: Some(ansi),
382 ..self
383 }
384 }
385}
386
387impl<'a> MakeVisitor<Writer<'a>> for PrettyFields {
388 type Visitor = PrettyVisitor<'a>;
389
390 #[inline]
391 fn make_visitor(&self, mut target: Writer<'a>) -> Self::Visitor {
392 if let Some(ansi: bool) = self.ansi {
393 target = target.with_ansi(is_ansi:ansi);
394 }
395 PrettyVisitor::new(writer:target, is_empty:true)
396 }
397}
398
399// === impl PrettyVisitor ===
400
401impl<'a> PrettyVisitor<'a> {
402 /// Returns a new default visitor that formats to the provided `writer`.
403 ///
404 /// # Arguments
405 /// - `writer`: the writer to format to.
406 /// - `is_empty`: whether or not any fields have been previously written to
407 /// that writer.
408 pub fn new(writer: Writer<'a>, is_empty: bool) -> Self {
409 Self {
410 writer,
411 is_empty,
412 style: Style::default(),
413 result: Ok(()),
414 }
415 }
416
417 pub(crate) fn with_style(self, style: Style) -> Self {
418 Self { style, ..self }
419 }
420
421 fn write_padded(&mut self, value: &impl fmt::Debug) {
422 let padding = if self.is_empty {
423 self.is_empty = false;
424 ""
425 } else {
426 ", "
427 };
428 self.result = write!(self.writer, "{}{:?}", padding, value);
429 }
430
431 fn bold(&self) -> Style {
432 if self.writer.has_ansi_escapes() {
433 self.style.bold()
434 } else {
435 Style::new()
436 }
437 }
438}
439
440impl<'a> field::Visit for PrettyVisitor<'a> {
441 fn record_str(&mut self, field: &Field, value: &str) {
442 if self.result.is_err() {
443 return;
444 }
445
446 if field.name() == "message" {
447 self.record_debug(field, &format_args!("{}", value))
448 } else {
449 self.record_debug(field, &value)
450 }
451 }
452
453 fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
454 if let Some(source) = value.source() {
455 let bold = self.bold();
456 self.record_debug(
457 field,
458 &format_args!(
459 "{}, {}{}.sources{}: {}",
460 value,
461 bold.prefix(),
462 field,
463 bold.infix(self.style),
464 ErrorSourceList(source),
465 ),
466 )
467 } else {
468 self.record_debug(field, &format_args!("{}", value))
469 }
470 }
471
472 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
473 if self.result.is_err() {
474 return;
475 }
476 let bold = self.bold();
477 match field.name() {
478 "message" => self.write_padded(&format_args!("{}{:?}", self.style.prefix(), value,)),
479 // Skip fields that are actually log metadata that have already been handled
480 #[cfg(feature = "tracing-log")]
481 name if name.starts_with("log.") => self.result = Ok(()),
482 name if name.starts_with("r#") => self.write_padded(&format_args!(
483 "{}{}{}: {:?}",
484 bold.prefix(),
485 &name[2..],
486 bold.infix(self.style),
487 value
488 )),
489 name => self.write_padded(&format_args!(
490 "{}{}{}: {:?}",
491 bold.prefix(),
492 name,
493 bold.infix(self.style),
494 value
495 )),
496 };
497 }
498}
499
500impl<'a> VisitOutput<fmt::Result> for PrettyVisitor<'a> {
501 fn finish(mut self) -> fmt::Result {
502 write!(&mut self.writer, "{}", self.style.suffix())?;
503 self.result
504 }
505}
506
507impl<'a> VisitFmt for PrettyVisitor<'a> {
508 fn writer(&mut self) -> &mut dyn fmt::Write {
509 &mut self.writer
510 }
511}
512