Skip to main content

rustfmt_nightly/
format_report_formatter.rs

1use crate::formatting::FormattingError;
2use crate::{ErrorKind, FormatReport};
3use annotate_snippets::{Annotation, Level, Renderer, Snippet};
4use std::fmt::{self, Display};
5
6/// A builder for [`FormatReportFormatter`].
7pub struct FormatReportFormatterBuilder<'a> {
8    report: &'a FormatReport,
9    enable_colors: bool,
10}
11
12impl<'a> FormatReportFormatterBuilder<'a> {
13    /// Creates a new [`FormatReportFormatterBuilder`].
14    pub fn new(report: &'a FormatReport) -> Self {
15        Self {
16            report,
17            enable_colors: false,
18        }
19    }
20
21    /// Enables colors and formatting in the output.
22    #[must_use]
23    pub fn enable_colors(self, enable_colors: bool) -> Self {
24        Self {
25            enable_colors,
26            ..self
27        }
28    }
29
30    /// Creates a new [`FormatReportFormatter`] from the settings in this builder.
31    pub fn build(self) -> FormatReportFormatter<'a> {
32        FormatReportFormatter {
33            report: self.report,
34            enable_colors: self.enable_colors,
35        }
36    }
37}
38
39/// Formats the warnings/errors in a [`FormatReport`].
40///
41/// Can be created using a [`FormatReportFormatterBuilder`].
42pub 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}