rustfmt_nightly/
format_report_formatter.rs1use crate::formatting::FormattingError;
2use crate::{ErrorKind, FormatReport};
3use annotate_snippets::{Annotation, Level, Renderer, Snippet};
4use std::fmt::{self, Display};
5
6pub struct FormatReportFormatterBuilder<'a> {
8 report: &'a FormatReport,
9 enable_colors: bool,
10}
11
12impl<'a> FormatReportFormatterBuilder<'a> {
13 pub fn new(report: &'a FormatReport) -> Self {
15 Self {
16 report,
17 enable_colors: false,
18 }
19 }
20
21 #[must_use]
23 pub fn enable_colors(self, enable_colors: bool) -> Self {
24 Self {
25 enable_colors,
26 ..self
27 }
28 }
29
30 pub fn build(self) -> FormatReportFormatter<'a> {
32 FormatReportFormatter {
33 report: self.report,
34 enable_colors: self.enable_colors,
35 }
36 }
37}
38
39pub struct FormatReportFormatter<'a> {
43 report: &'a FormatReport,
44 enable_colors: bool,
45}
46
47impl<'a> Display for FormatReportFormatter<'a> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 let errors_by_file = &self.report.internal.borrow().0;
50
51 let renderer = if self.enable_colors {
52 Renderer::styled()
53 } else {
54 Renderer::plain()
55 };
56
57 for (file, errors) in errors_by_file {
58 for error in errors {
59 let error_kind = error.kind.to_string();
60 let mut message =
61 error_kind_to_snippet_annotation_level(&error.kind).title(&error_kind);
62 if error.is_internal() {
63 message = message.id("internal");
64 }
65
66 let message_suffix = error.msg_suffix();
67 if !message_suffix.is_empty() {
68 message = message.footer(Level::Note.title(&message_suffix));
69 }
70
71 let origin = format!("{}:{}", file, error.line);
72 let snippet = Snippet::source(&error.line_buffer)
73 .line_start(error.line)
74 .origin(&origin)
75 .fold(false)
76 .annotations(annotation(error));
77 message = message.snippet(snippet);
78
79 writeln!(f, "{}\n", renderer.render(message))?;
80 }
81 }
82
83 if !errors_by_file.is_empty() {
84 let label = format!(
85 "rustfmt has failed to format. See previous {} errors.",
86 self.report.warning_count()
87 );
88 let message = Level::Warning.title(&label);
89 writeln!(f, "{}", renderer.render(message))?;
90 }
91
92 Ok(())
93 }
94}
95
96fn annotation(error: &FormattingError) -> Option<Annotation<'_>> {
97 let (range_start, range_length) = error.format_len();
98 let range_end = range_start + range_length;
99
100 if range_length > 0 {
101 Some(Level::Error.span(range_start..range_end))
102 } else {
103 None
104 }
105}
106
107fn error_kind_to_snippet_annotation_level(error_kind: &ErrorKind) -> Level {
108 match error_kind {
109 ErrorKind::LineOverflow(..)
110 | ErrorKind::TrailingWhitespace
111 | ErrorKind::IoError(_)
112 | ErrorKind::ModuleResolutionError(_)
113 | ErrorKind::ParseError
114 | ErrorKind::LostComment
115 | ErrorKind::BadAttr
116 | ErrorKind::InvalidGlobPattern(_)
117 | ErrorKind::VersionMismatch => Level::Error,
118 ErrorKind::DeprecatedAttr => Level::Warning,
119 }
120}