rustfmt_nightly/emitter/
checkstyle.rs
1use self::xml::XmlEscaped;
2use super::*;
3use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
4
5mod xml;
6
7#[derive(Debug, Default)]
8pub(crate) struct CheckstyleEmitter;
9
10impl Emitter for CheckstyleEmitter {
11 fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
12 writeln!(output, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
13 write!(output, r#"<checkstyle version="4.3">"#)?;
14 Ok(())
15 }
16
17 fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
18 writeln!(output, "</checkstyle>")
19 }
20
21 fn emit_formatted_file(
22 &mut self,
23 output: &mut dyn Write,
24 FormattedFile {
25 filename,
26 original_text,
27 formatted_text,
28 }: FormattedFile<'_>,
29 ) -> Result<EmitterResult, io::Error> {
30 const CONTEXT_SIZE: usize = 0;
31 let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
32 output_checkstyle_file(output, filename, diff)?;
33 Ok(EmitterResult::default())
34 }
35}
36
37pub(crate) fn output_checkstyle_file<T>(
38 mut writer: T,
39 filename: &FileName,
40 diff: Vec<Mismatch>,
41) -> Result<(), io::Error>
42where
43 T: Write,
44{
45 write!(writer, r#"<file name="{filename}">"#)?;
46 for mismatch in diff {
47 let begin_line = mismatch.line_number;
48 let mut current_line;
49 let mut line_counter = 0;
50 for line in mismatch.lines {
51 if let DiffLine::Expected(message) = line {
53 current_line = begin_line + line_counter;
54 line_counter += 1;
55 write!(
56 writer,
57 r#"<error line="{}" severity="warning" message="Should be `{}`" />"#,
58 current_line,
59 XmlEscaped(&message)
60 )?;
61 }
62 }
63 }
64 write!(writer, "</file>")?;
65 Ok(())
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use std::path::PathBuf;
72
73 #[test]
74 fn emits_empty_record_on_file_with_no_mismatches() {
75 let file_name = "src/well_formatted.rs";
76 let mut writer = Vec::new();
77 let _ = output_checkstyle_file(
78 &mut writer,
79 &FileName::Real(PathBuf::from(file_name)),
80 vec![],
81 );
82 assert_eq!(
83 &writer[..],
84 format!(r#"<file name="{file_name}"></file>"#).as_bytes()
85 );
86 }
87
88 #[test]
90 fn emits_single_xml_tree_containing_all_files() {
91 let bin_file = "src/bin.rs";
92 let bin_original = ["fn main() {", "println!(\"Hello, world!\");", "}"];
93 let bin_formatted = ["fn main() {", " println!(\"Hello, world!\");", "}"];
94 let lib_file = "src/lib.rs";
95 let lib_original = ["fn greet() {", "println!(\"Greetings!\");", "}"];
96 let lib_formatted = ["fn greet() {", " println!(\"Greetings!\");", "}"];
97 let mut writer = Vec::new();
98 let mut emitter = CheckstyleEmitter::default();
99 let _ = emitter.emit_header(&mut writer);
100 let _ = emitter
101 .emit_formatted_file(
102 &mut writer,
103 FormattedFile {
104 filename: &FileName::Real(PathBuf::from(bin_file)),
105 original_text: &bin_original.join("\n"),
106 formatted_text: &bin_formatted.join("\n"),
107 },
108 )
109 .unwrap();
110 let _ = emitter
111 .emit_formatted_file(
112 &mut writer,
113 FormattedFile {
114 filename: &FileName::Real(PathBuf::from(lib_file)),
115 original_text: &lib_original.join("\n"),
116 formatted_text: &lib_formatted.join("\n"),
117 },
118 )
119 .unwrap();
120 let _ = emitter.emit_footer(&mut writer);
121 let exp_bin_xml = [
122 format!(r#"<file name="{}">"#, bin_file),
123 format!(
124 r#"<error line="2" severity="warning" message="Should be `{}`" />"#,
125 XmlEscaped(r#" println!("Hello, world!");"#),
126 ),
127 String::from("</file>"),
128 ];
129 let exp_lib_xml = [
130 format!(r#"<file name="{}">"#, lib_file),
131 format!(
132 r#"<error line="2" severity="warning" message="Should be `{}`" />"#,
133 XmlEscaped(r#" println!("Greetings!");"#),
134 ),
135 String::from("</file>"),
136 ];
137 assert_eq!(
138 String::from_utf8(writer).unwrap(),
139 [
140 r#"<?xml version="1.0" encoding="utf-8"?>"#,
141 "\n",
142 r#"<checkstyle version="4.3">"#,
143 &format!("{}{}", exp_bin_xml.join(""), exp_lib_xml.join("")),
144 "</checkstyle>\n",
145 ]
146 .join(""),
147 );
148 }
149}