diff options
Diffstat (limited to 'src/tools/rustfmt/src/emitter/checkstyle.rs')
-rw-r--r-- | src/tools/rustfmt/src/emitter/checkstyle.rs | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs new file mode 100644 index 000000000..545b25997 --- /dev/null +++ b/src/tools/rustfmt/src/emitter/checkstyle.rs @@ -0,0 +1,150 @@ +use self::xml::XmlEscaped; +use super::*; +use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use std::io::{self, Write}; + +mod xml; + +#[derive(Debug, Default)] +pub(crate) struct CheckstyleEmitter; + +impl Emitter for CheckstyleEmitter { + fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> { + writeln!(output, r#"<?xml version="1.0" encoding="utf-8"?>"#)?; + write!(output, r#"<checkstyle version="4.3">"#)?; + Ok(()) + } + + fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { + writeln!(output, "</checkstyle>") + } + + fn emit_formatted_file( + &mut self, + output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result<EmitterResult, io::Error> { + const CONTEXT_SIZE: usize = 0; + let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); + output_checkstyle_file(output, filename, diff)?; + Ok(EmitterResult::default()) + } +} + +pub(crate) fn output_checkstyle_file<T>( + mut writer: T, + filename: &FileName, + diff: Vec<Mismatch>, +) -> Result<(), io::Error> +where + T: Write, +{ + write!(writer, r#"<file name="{}">"#, filename)?; + for mismatch in diff { + let begin_line = mismatch.line_number; + let mut current_line; + let mut line_counter = 0; + for line in mismatch.lines { + // Do nothing with `DiffLine::Context` and `DiffLine::Resulting`. + if let DiffLine::Expected(message) = line { + current_line = begin_line + line_counter; + line_counter += 1; + write!( + writer, + r#"<error line="{}" severity="warning" message="Should be `{}`" />"#, + current_line, + XmlEscaped(&message) + )?; + } + } + } + write!(writer, "</file>")?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + fn emits_empty_record_on_file_with_no_mismatches() { + let file_name = "src/well_formatted.rs"; + let mut writer = Vec::new(); + let _ = output_checkstyle_file( + &mut writer, + &FileName::Real(PathBuf::from(file_name)), + vec![], + ); + assert_eq!( + &writer[..], + format!(r#"<file name="{}"></file>"#, file_name).as_bytes() + ); + } + + // https://github.com/rust-lang/rustfmt/issues/1636 + #[test] + fn emits_single_xml_tree_containing_all_files() { + let bin_file = "src/bin.rs"; + let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"]; + let bin_formatted = vec!["fn main() {", " println!(\"Hello, world!\");", "}"]; + let lib_file = "src/lib.rs"; + let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"]; + let lib_formatted = vec!["fn greet() {", " println!(\"Greetings!\");", "}"]; + let mut writer = Vec::new(); + let mut emitter = CheckstyleEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(bin_file)), + original_text: &bin_original.join("\n"), + formatted_text: &bin_formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(lib_file)), + original_text: &lib_original.join("\n"), + formatted_text: &lib_formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + let exp_bin_xml = vec![ + format!(r#"<file name="{}">"#, bin_file), + format!( + r#"<error line="2" severity="warning" message="Should be `{}`" />"#, + XmlEscaped(r#" println!("Hello, world!");"#), + ), + String::from("</file>"), + ]; + let exp_lib_xml = vec![ + format!(r#"<file name="{}">"#, lib_file), + format!( + r#"<error line="2" severity="warning" message="Should be `{}`" />"#, + XmlEscaped(r#" println!("Greetings!");"#), + ), + String::from("</file>"), + ]; + assert_eq!( + String::from_utf8(writer).unwrap(), + vec![ + r#"<?xml version="1.0" encoding="utf-8"?>"#, + "\n", + r#"<checkstyle version="4.3">"#, + &format!("{}{}", exp_bin_xml.join(""), exp_lib_xml.join("")), + "</checkstyle>\n", + ] + .join(""), + ); + } +} |