rustfmt_nightly/emitter/
diff.rs

1use super::*;
2use crate::config::Config;
3use crate::rustfmt_diff::{make_diff, print_diff};
4
5pub(crate) struct DiffEmitter {
6    config: Config,
7}
8
9impl DiffEmitter {
10    pub(crate) fn new(config: Config) -> Self {
11        Self { config }
12    }
13}
14
15impl Emitter for DiffEmitter {
16    fn emit_formatted_file(
17        &mut self,
18        output: &mut dyn Write,
19        FormattedFile {
20            filename,
21            original_text,
22            formatted_text,
23        }: FormattedFile<'_>,
24    ) -> Result<EmitterResult, io::Error> {
25        const CONTEXT_SIZE: usize = 3;
26        let mismatch = make_diff(original_text, formatted_text, CONTEXT_SIZE);
27        let has_diff = !mismatch.is_empty();
28
29        if has_diff {
30            if self.config.print_misformatted_file_names() {
31                writeln!(output, "{filename}")?;
32            } else {
33                print_diff(
34                    mismatch,
35                    |line_num| format!("Diff in {}:{}:", filename, line_num),
36                    &self.config,
37                );
38            }
39        } else if original_text != formatted_text {
40            // This occurs when the only difference between the original and formatted values
41            // is the newline style. This happens because The make_diff function compares the
42            // original and formatted values line by line, independent of line endings.
43            writeln!(output, "Incorrect newline style in {filename}")?;
44            return Ok(EmitterResult { has_diff: true });
45        }
46
47        Ok(EmitterResult { has_diff })
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use std::path::PathBuf;
55
56    #[test]
57    fn does_not_print_when_no_files_reformatted() {
58        let mut writer = Vec::new();
59        let config = Config::default();
60        let mut emitter = DiffEmitter::new(config);
61        let result = emitter
62            .emit_formatted_file(
63                &mut writer,
64                FormattedFile {
65                    filename: &FileName::Real(PathBuf::from("src/lib.rs")),
66                    original_text: "fn empty() {}\n",
67                    formatted_text: "fn empty() {}\n",
68                },
69            )
70            .unwrap();
71        assert_eq!(result.has_diff, false);
72        assert_eq!(writer.len(), 0);
73    }
74
75    #[test]
76    fn prints_file_names_when_config_is_enabled() {
77        let bin_file = "src/bin.rs";
78        let bin_original = "fn main() {\nprintln!(\"Hello, world!\");\n}";
79        let bin_formatted = "fn main() {\n    println!(\"Hello, world!\");\n}";
80        let lib_file = "src/lib.rs";
81        let lib_original = "fn greet() {\nprintln!(\"Greetings!\");\n}";
82        let lib_formatted = "fn greet() {\n    println!(\"Greetings!\");\n}";
83
84        let mut writer = Vec::new();
85        let mut config = Config::default();
86        config.set().print_misformatted_file_names(true);
87        let mut emitter = DiffEmitter::new(config);
88        let _ = emitter
89            .emit_formatted_file(
90                &mut writer,
91                FormattedFile {
92                    filename: &FileName::Real(PathBuf::from(bin_file)),
93                    original_text: bin_original,
94                    formatted_text: bin_formatted,
95                },
96            )
97            .unwrap();
98        let _ = emitter
99            .emit_formatted_file(
100                &mut writer,
101                FormattedFile {
102                    filename: &FileName::Real(PathBuf::from(lib_file)),
103                    original_text: lib_original,
104                    formatted_text: lib_formatted,
105                },
106            )
107            .unwrap();
108
109        assert_eq!(
110            String::from_utf8(writer).unwrap(),
111            format!("{bin_file}\n{lib_file}\n"),
112        )
113    }
114
115    #[test]
116    fn prints_newline_message_with_only_newline_style_diff() {
117        let mut writer = Vec::new();
118        let config = Config::default();
119        let mut emitter = DiffEmitter::new(config);
120        let _ = emitter
121            .emit_formatted_file(
122                &mut writer,
123                FormattedFile {
124                    filename: &FileName::Real(PathBuf::from("src/lib.rs")),
125                    original_text: "fn empty() {}\n",
126                    formatted_text: "fn empty() {}\r\n",
127                },
128            )
129            .unwrap();
130        assert_eq!(
131            String::from_utf8(writer).unwrap(),
132            String::from("Incorrect newline style in src/lib.rs\n")
133        );
134    }
135}