From 70ba6fbcc4790f57b13ee71e8af8cd2350b41840 Mon Sep 17 00:00:00 2001 From: calebcartwright Date: Thu, 8 Aug 2019 23:29:28 -0500 Subject: [PATCH 1/4] feat: add json emitter --- src/bin/main.rs | 1 + src/config/options.rs | 3 + src/emitter.rs | 4 +- src/emitter/checkstyle.rs | 2 +- src/emitter/json.rs | 152 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +- src/source_file.rs | 2 +- 7 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 src/emitter/json.rs diff --git a/src/bin/main.rs b/src/bin/main.rs index 70990aa798d..1ebad122a31 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -631,6 +631,7 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result { "stdout" => Ok(EmitMode::Stdout), "coverage" => Ok(EmitMode::Coverage), "checkstyle" => Ok(EmitMode::Checkstyle), + "json" => Ok(EmitMode::Json), _ => Err(format_err!("Invalid value for `--emit`")), } } diff --git a/src/config/options.rs b/src/config/options.rs index cf1a328e9cd..d2063650ccb 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -119,6 +119,9 @@ pub enum EmitMode { Coverage, /// Unfancy stdout Checkstyle, + /// Writes the resulting diffs in a JSON format. Returns an empty array + /// `[]` if there were no diffs. + Json, /// Output the changed lines (for internal value only) ModifiedLines, /// Checks if a diff can be generated. If so, rustfmt outputs a diff and diff --git a/src/emitter.rs b/src/emitter.rs index 03ca1e35b72..c1657fe7e6c 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -2,6 +2,7 @@ pub(crate) use self::checkstyle::*; pub(crate) use self::diff::*; pub(crate) use self::files::*; pub(crate) use self::files_with_backup::*; +pub(crate) use self::json::*; pub(crate) use self::modified_lines::*; pub(crate) use self::stdout::*; use crate::FileName; @@ -12,6 +13,7 @@ mod checkstyle; mod diff; mod files; mod files_with_backup; +mod json; mod modified_lines; mod stdout; @@ -37,7 +39,7 @@ pub(crate) trait Emitter { Ok(()) } - fn emit_footer(&self, _output: &mut dyn Write) -> Result<(), io::Error> { + fn emit_footer(&self, _output: &mut dyn Write, _has_diff: bool) -> Result<(), io::Error> { Ok(()) } } diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index eb1499985fd..09664d8f381 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -16,7 +16,7 @@ impl Emitter for CheckstyleEmitter { Ok(()) } - fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { + fn emit_footer(&self, output: &mut dyn Write, _has_diff: bool) -> Result<(), io::Error> { writeln!(output, "") } diff --git a/src/emitter/json.rs b/src/emitter/json.rs new file mode 100644 index 00000000000..faa7c0bf335 --- /dev/null +++ b/src/emitter/json.rs @@ -0,0 +1,152 @@ +use super::*; +use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use serde::Serialize; +use serde_json::to_string as to_json_string; +use std::io::{self, Write}; +use std::path::Path; + +#[derive(Debug, Default)] +pub(crate) struct JsonEmitter; + +#[derive(Debug, Default, Serialize)] +struct MismatchedBlock { + original_begin_line: u32, + original_end_line: u32, + expected_begin_line: u32, + expected_end_line: u32, + original: String, + expected: String, +} + +#[derive(Debug, Default, Serialize)] +struct MismatchedFile { + name: String, + mismatches: Vec, +} + +impl Emitter for JsonEmitter { + fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> { + write!(output, "[")?; + Ok(()) + } + + fn emit_footer(&self, output: &mut dyn Write, has_diff: bool) -> Result<(), io::Error> { + let prefix = if has_diff { "\u{8}" } else { "" }; + write!(output, "{}]", prefix)?; + Ok(()) + } + + fn emit_formatted_file( + &self, + output: &mut dyn Write, + FormattedFile { + filename, + original_text, + formatted_text, + }: FormattedFile<'_>, + ) -> Result { + const CONTEXT_SIZE: usize = 0; + let filename = ensure_real_path(filename); + let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); + let has_diff = !diff.is_empty(); + + if has_diff { + output_json_file(output, filename, diff)?; + } + + Ok(EmitterResult { has_diff }) + } +} + +fn output_json_file(mut writer: T, filename: &Path, diff: Vec) -> Result<(), io::Error> +where + T: Write, +{ + let mut mismatches = vec![]; + for mismatch in diff { + let mut original_line_counter = 0; + let mut expected_line_counter = 0; + let original_begin_line = mismatch.line_number_orig; + let expected_begin_line = mismatch.line_number; + let mut original_lines = vec![]; + let mut expected_lines = vec![]; + + for line in mismatch.lines { + match line { + DiffLine::Expected(msg) => { + expected_line_counter += 1; + expected_lines.push(msg) + } + DiffLine::Resulting(msg) => { + original_line_counter += 1; + original_lines.push(msg) + } + DiffLine::Context(_) => continue, + } + } + + let original_end_line = if original_line_counter > 0 { + original_begin_line + original_line_counter - 1 + } else { + original_begin_line + original_line_counter + }; + let expected_end_line = if expected_line_counter > 0 { + expected_begin_line + expected_line_counter - 1 + } else { + expected_begin_line + expected_line_counter + }; + + mismatches.push(MismatchedBlock { + original_begin_line, + original_end_line, + expected_begin_line, + expected_end_line, + original: original_lines.join("\n"), + expected: expected_lines.join("\n"), + }); + } + let json = to_json_string(&MismatchedFile { + name: String::from(filename.to_str().unwrap()), + mismatches, + })?; + write!(writer, "{},", &json)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + fn expected_line_range_correct_when_single_line_split() { + let file = "foo/bar.rs"; + let mismatched_file = MismatchedFile { + name: String::from(file), + mismatches: vec![MismatchedBlock { + original_begin_line: 79, + original_end_line: 79, + expected_begin_line: 79, + expected_end_line: 82, + original: String::from("fn Foo() where T: Bar {"), + expected: String::from("fn Foo()\nwhere\n T: Bar,\n{"), + }], + }; + let mismatch = Mismatch { + line_number: 79, + line_number_orig: 79, + lines: vec![ + DiffLine::Resulting(String::from("fn Foo() where T: Bar {")), + DiffLine::Expected(String::from("fn Foo()")), + DiffLine::Expected(String::from("where")), + DiffLine::Expected(String::from(" T: Bar,")), + DiffLine::Expected(String::from("{")), + ], + }; + + let mut writer = Vec::new(); + let exp_json = to_json_string(&mismatched_file).unwrap(); + let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch]); + assert_eq!(&writer[..], format!("{},", exp_json).as_bytes()); + } +} diff --git a/src/lib.rs b/src/lib.rs index e3e7f7f9244..d78e4e000f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -486,6 +486,7 @@ pub(crate) fn create_emitter<'a>(config: &Config) -> Box { EmitMode::Stdout | EmitMode::Coverage => { Box::new(emitter::StdoutEmitter::new(config.verbose())) } + EmitMode::Json => Box::new(emitter::JsonEmitter::default()), EmitMode::ModifiedLines => Box::new(emitter::ModifiedLinesEmitter::default()), EmitMode::Checkstyle => Box::new(emitter::CheckstyleEmitter::default()), EmitMode::Diff => Box::new(emitter::DiffEmitter::new(config.clone())), @@ -495,7 +496,7 @@ pub(crate) fn create_emitter<'a>(config: &Config) -> Box { impl<'b, T: Write + 'b> Drop for Session<'b, T> { fn drop(&mut self) { if let Some(ref mut out) = self.out { - let _ = self.emitter.emit_footer(out); + let _ = self.emitter.emit_footer(out, self.errors.has_diff); } } } diff --git a/src/source_file.rs b/src/source_file.rs index 074b7a7315f..f52e99c43c0 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -34,7 +34,7 @@ where for &(ref filename, ref text) in source_file { write_file(None, filename, text, out, &*emitter)?; } - emitter.emit_footer(out)?; + emitter.emit_footer(out, false)?; Ok(()) } From 119086b97f80a79a3398498866feb98270d7638d Mon Sep 17 00:00:00 2001 From: calebcartwright Date: Fri, 9 Aug 2019 18:12:38 -0500 Subject: [PATCH 2/4] refactor: json emitter --- src/emitter.rs | 4 +-- src/emitter/checkstyle.rs | 4 +-- src/emitter/diff.rs | 2 +- src/emitter/files.rs | 2 +- src/emitter/files_with_backup.rs | 2 +- src/emitter/json.rs | 48 +++++++++++++++++--------------- src/emitter/modified_lines.rs | 2 +- src/emitter/stdout.rs | 2 +- src/formatting.rs | 3 +- src/lib.rs | 2 +- src/source_file.rs | 8 +++--- 11 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/emitter.rs b/src/emitter.rs index c1657fe7e6c..dc2c99a301e 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -30,7 +30,7 @@ pub(crate) struct EmitterResult { pub(crate) trait Emitter { fn emit_formatted_file( - &self, + &mut self, output: &mut dyn Write, formatted_file: FormattedFile<'_>, ) -> Result; @@ -39,7 +39,7 @@ pub(crate) trait Emitter { Ok(()) } - fn emit_footer(&self, _output: &mut dyn Write, _has_diff: bool) -> Result<(), io::Error> { + fn emit_footer(&self, _output: &mut dyn Write) -> Result<(), io::Error> { Ok(()) } } diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 09664d8f381..e8f8ec86cf1 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -16,12 +16,12 @@ impl Emitter for CheckstyleEmitter { Ok(()) } - fn emit_footer(&self, output: &mut dyn Write, _has_diff: bool) -> Result<(), io::Error> { + fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { writeln!(output, "") } fn emit_formatted_file( - &self, + &mut self, output: &mut dyn Write, FormattedFile { filename, diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 5e387395b5b..08a42b4679d 100644 --- a/src/emitter/diff.rs +++ b/src/emitter/diff.rs @@ -14,7 +14,7 @@ impl DiffEmitter { impl Emitter for DiffEmitter { fn emit_formatted_file( - &self, + &mut self, _output: &mut dyn Write, FormattedFile { filename, diff --git a/src/emitter/files.rs b/src/emitter/files.rs index 5b1dbce11c9..4538a8f4c49 100644 --- a/src/emitter/files.rs +++ b/src/emitter/files.rs @@ -6,7 +6,7 @@ pub(crate) struct FilesEmitter; impl Emitter for FilesEmitter { fn emit_formatted_file( - &self, + &mut self, _output: &mut dyn Write, FormattedFile { filename, diff --git a/src/emitter/files_with_backup.rs b/src/emitter/files_with_backup.rs index af3e0e2d91d..4c15f6fa5ec 100644 --- a/src/emitter/files_with_backup.rs +++ b/src/emitter/files_with_backup.rs @@ -6,7 +6,7 @@ pub(crate) struct FilesWithBackupEmitter; impl Emitter for FilesWithBackupEmitter { fn emit_formatted_file( - &self, + &mut self, _output: &mut dyn Write, FormattedFile { filename, diff --git a/src/emitter/json.rs b/src/emitter/json.rs index faa7c0bf335..e84a88e806e 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -6,7 +6,9 @@ use std::io::{self, Write}; use std::path::Path; #[derive(Debug, Default)] -pub(crate) struct JsonEmitter; +pub(crate) struct JsonEmitter { + num_files: u32, +} #[derive(Debug, Default, Serialize)] struct MismatchedBlock { @@ -30,14 +32,13 @@ impl Emitter for JsonEmitter { Ok(()) } - fn emit_footer(&self, output: &mut dyn Write, has_diff: bool) -> Result<(), io::Error> { - let prefix = if has_diff { "\u{8}" } else { "" }; - write!(output, "{}]", prefix)?; + fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { + write!(output, "]")?; Ok(()) } fn emit_formatted_file( - &self, + &mut self, output: &mut dyn Write, FormattedFile { filename, @@ -51,33 +52,43 @@ impl Emitter for JsonEmitter { let has_diff = !diff.is_empty(); if has_diff { - output_json_file(output, filename, diff)?; + output_json_file(output, filename, diff, self.num_files)?; + self.num_files += 1; } Ok(EmitterResult { has_diff }) } } -fn output_json_file(mut writer: T, filename: &Path, diff: Vec) -> Result<(), io::Error> +fn output_json_file( + mut writer: T, + filename: &Path, + diff: Vec, + num_emitted_files: u32, +) -> Result<(), io::Error> where T: Write, { let mut mismatches = vec![]; for mismatch in diff { - let mut original_line_counter = 0; - let mut expected_line_counter = 0; let original_begin_line = mismatch.line_number_orig; let expected_begin_line = mismatch.line_number; + let mut original_end_line = original_begin_line; + let mut expected_end_line = expected_begin_line; + let mut original_line_counter = 0; + let mut expected_line_counter = 0; let mut original_lines = vec![]; let mut expected_lines = vec![]; for line in mismatch.lines { match line { DiffLine::Expected(msg) => { + expected_end_line = expected_begin_line + expected_line_counter; expected_line_counter += 1; expected_lines.push(msg) } DiffLine::Resulting(msg) => { + original_end_line = original_begin_line + original_line_counter; original_line_counter += 1; original_lines.push(msg) } @@ -85,17 +96,6 @@ where } } - let original_end_line = if original_line_counter > 0 { - original_begin_line + original_line_counter - 1 - } else { - original_begin_line + original_line_counter - }; - let expected_end_line = if expected_line_counter > 0 { - expected_begin_line + expected_line_counter - 1 - } else { - expected_begin_line + expected_line_counter - }; - mismatches.push(MismatchedBlock { original_begin_line, original_end_line, @@ -109,13 +109,15 @@ where name: String::from(filename.to_str().unwrap()), mismatches, })?; - write!(writer, "{},", &json)?; + let prefix = if num_emitted_files > 0 { "," } else { "" }; + write!(writer, "{}{}", prefix, &json)?; Ok(()) } #[cfg(test)] mod tests { use super::*; + use crate::FileName; use std::path::PathBuf; #[test] @@ -146,7 +148,7 @@ mod tests { let mut writer = Vec::new(); let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch]); - assert_eq!(&writer[..], format!("{},", exp_json).as_bytes()); + let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); + assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); } } diff --git a/src/emitter/modified_lines.rs b/src/emitter/modified_lines.rs index 83736c47bd6..94ff570a8a9 100644 --- a/src/emitter/modified_lines.rs +++ b/src/emitter/modified_lines.rs @@ -7,7 +7,7 @@ pub(crate) struct ModifiedLinesEmitter; impl Emitter for ModifiedLinesEmitter { fn emit_formatted_file( - &self, + &mut self, output: &mut dyn Write, FormattedFile { original_text, diff --git a/src/emitter/stdout.rs b/src/emitter/stdout.rs index 968de68c741..9fddd515e49 100644 --- a/src/emitter/stdout.rs +++ b/src/emitter/stdout.rs @@ -15,7 +15,7 @@ impl StdoutEmitter { impl Emitter for StdoutEmitter { fn emit_formatted_file( - &self, + &mut self, output: &mut dyn Write, FormattedFile { filename, diff --git a/src/formatting.rs b/src/formatting.rs index 879ddc61b13..ee44549b2bc 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -235,7 +235,8 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { report: &mut FormatReport, ) -> Result<(), ErrorKind> { if let Some(ref mut out) = self.out { - match source_file::write_file(Some(source_map), &path, &result, out, &*self.emitter) { + match source_file::write_file(Some(source_map), &path, &result, out, &mut *self.emitter) + { Ok(ref result) if result.has_diff => report.add_diff(), Err(e) => { // Create a new error with path_str to help users see which files failed diff --git a/src/lib.rs b/src/lib.rs index d78e4e000f2..dbb7d2e0e17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -496,7 +496,7 @@ pub(crate) fn create_emitter<'a>(config: &Config) -> Box { impl<'b, T: Write + 'b> Drop for Session<'b, T> { fn drop(&mut self) { if let Some(ref mut out) = self.out { - let _ = self.emitter.emit_footer(out, self.errors.has_diff); + let _ = self.emitter.emit_footer(out); } } } diff --git a/src/source_file.rs b/src/source_file.rs index f52e99c43c0..28366ca2485 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -28,13 +28,13 @@ pub(crate) fn write_all_files( where T: Write, { - let emitter = create_emitter(config); + let mut emitter = create_emitter(config); emitter.emit_header(out)?; for &(ref filename, ref text) in source_file { - write_file(None, filename, text, out, &*emitter)?; + write_file(None, filename, text, out, &mut *emitter)?; } - emitter.emit_footer(out, false)?; + emitter.emit_footer(out)?; Ok(()) } @@ -44,7 +44,7 @@ pub(crate) fn write_file( filename: &FileName, formatted_text: &str, out: &mut T, - emitter: &dyn Emitter, + emitter: &mut dyn Emitter, ) -> Result where T: Write, From 8a4e564ff1f2e004b27e06965c8fce62beda1cbc Mon Sep 17 00:00:00 2001 From: calebcartwright Date: Fri, 9 Aug 2019 18:35:27 -0500 Subject: [PATCH 3/4] tests: add tests for new json emit mode --- src/emitter/json.rs | 195 +++++++++++++++++++++++++++++ src/test/mod.rs | 7 ++ tests/writemode/source/json.rs | 80 ++++++++++++ tests/writemode/target/output.json | 1 + 4 files changed, 283 insertions(+) create mode 100644 tests/writemode/source/json.rs create mode 100644 tests/writemode/target/output.json diff --git a/src/emitter/json.rs b/src/emitter/json.rs index e84a88e806e..269dd2d4daf 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -151,4 +151,199 @@ mod tests { let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); } + + #[test] + fn context_lines_ignored() { + let file = "src/lib.rs"; + let mismatched_file = MismatchedFile { + name: String::from(file), + mismatches: vec![MismatchedBlock { + original_begin_line: 5, + original_end_line: 5, + expected_begin_line: 5, + expected_end_line: 5, + original: String::from( + "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {", + ), + expected: String::from( + "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {", + ), + }], + }; + let mismatch = Mismatch { + line_number: 5, + line_number_orig: 5, + lines: vec![ + DiffLine::Context(String::new()), + DiffLine::Resulting(String::from( + "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {", + )), + DiffLine::Context(String::new()), + DiffLine::Expected(String::from( + "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {", + )), + DiffLine::Context(String::new()), + ], + }; + + let mut writer = Vec::new(); + let exp_json = to_json_string(&mismatched_file).unwrap(); + let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); + assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); + } + + #[test] + fn emits_empty_array_on_no_diffs() { + let mut writer = Vec::new(); + let mut emitter = JsonEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let result = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from("src/lib.rs")), + original_text: "fn empty() {}\n", + formatted_text: "fn empty() {}\n", + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + assert_eq!(result.has_diff, false); + assert_eq!(&writer[..], "[]".as_bytes()); + } + + #[test] + fn emits_array_with_files_with_diffs() { + let file_name = "src/bin.rs"; + let original = vec![ + "fn main() {", + "println!(\"Hello, world!\");", + "}", + "", + "#[cfg(test)]", + "mod tests {", + "#[test]", + "fn it_works() {", + " assert_eq!(2 + 2, 4);", + "}", + "}", + ]; + let formatted = vec![ + "fn main() {", + " println!(\"Hello, world!\");", + "}", + "", + "#[cfg(test)]", + "mod tests {", + " #[test]", + " fn it_works() {", + " assert_eq!(2 + 2, 4);", + " }", + "}", + ]; + let mut writer = Vec::new(); + let mut emitter = JsonEmitter::default(); + let _ = emitter.emit_header(&mut writer); + let result = emitter + .emit_formatted_file( + &mut writer, + FormattedFile { + filename: &FileName::Real(PathBuf::from(file_name)), + original_text: &original.join("\n"), + formatted_text: &formatted.join("\n"), + }, + ) + .unwrap(); + let _ = emitter.emit_footer(&mut writer); + let exp_json = to_json_string(&MismatchedFile { + name: String::from(file_name), + mismatches: vec![ + MismatchedBlock { + original_begin_line: 2, + original_end_line: 2, + expected_begin_line: 2, + expected_end_line: 2, + original: String::from("println!(\"Hello, world!\");"), + expected: String::from(" println!(\"Hello, world!\");"), + }, + MismatchedBlock { + original_begin_line: 7, + original_end_line: 10, + expected_begin_line: 7, + expected_end_line: 10, + original: String::from( + "#[test]\nfn it_works() {\n assert_eq!(2 + 2, 4);\n}", + ), + expected: String::from( + " #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }", + ), + }, + ], + }) + .unwrap(); + assert_eq!(result.has_diff, true); + assert_eq!(&writer[..], format!("[{}]", exp_json).as_bytes()); + } + + #[test] + fn emits_valid_json_with_multiple_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 = JsonEmitter::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_json = to_json_string(&MismatchedFile { + name: String::from(bin_file), + mismatches: vec![MismatchedBlock { + original_begin_line: 2, + original_end_line: 2, + expected_begin_line: 2, + expected_end_line: 2, + original: String::from("println!(\"Hello, world!\");"), + expected: String::from(" println!(\"Hello, world!\");"), + }], + }) + .unwrap(); + let exp_lib_json = to_json_string(&MismatchedFile { + name: String::from(lib_file), + mismatches: vec![MismatchedBlock { + original_begin_line: 2, + original_end_line: 2, + expected_begin_line: 2, + expected_end_line: 2, + original: String::from("println!(\"Greetings!\");"), + expected: String::from(" println!(\"Greetings!\");"), + }], + }) + .unwrap(); + assert_eq!( + &writer[..], + format!("[{},{}]", exp_bin_json, exp_lib_json).as_bytes() + ); + } } diff --git a/src/test/mod.rs b/src/test/mod.rs index a7063c76f28..bd6613e1488 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -200,6 +200,13 @@ fn checkstyle_test() { assert_output(Path::new(filename), Path::new(expected_filename)); } +#[test] +fn json_test() { + let filename = "tests/writemode/source/json.rs"; + let expected_filename = "tests/writemode/target/output.json"; + assert_output(Path::new(filename), Path::new(expected_filename)); +} + #[test] fn modified_test() { use std::io::BufRead; diff --git a/tests/writemode/source/json.rs b/tests/writemode/source/json.rs new file mode 100644 index 00000000000..89dcf694183 --- /dev/null +++ b/tests/writemode/source/json.rs @@ -0,0 +1,80 @@ +// rustfmt-fn_single_line: true +// rustfmt-emit_mode: json +// Test single-line functions. + +fn foo_expr() { + 1 +} + +fn foo_stmt() { + foo(); +} + +fn foo_decl_local() { + let z = 5; + } + +fn foo_decl_item(x: &mut i32) { + x = 3; +} + + fn empty() { + +} + +fn foo_return() -> String { + "yay" +} + +fn foo_where() -> T where T: Sync { + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space () { + 1 +} + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} + +fn Foo() where T: Bar { +} diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json new file mode 100644 index 00000000000..b5f327b0a1c --- /dev/null +++ b/tests/writemode/target/output.json @@ -0,0 +1 @@ +[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] \ No newline at end of file From 8067cb013b4083b9e588f0ae3ffd5d45377370fd Mon Sep 17 00:00:00 2001 From: calebcartwright Date: Fri, 9 Aug 2019 18:55:25 -0500 Subject: [PATCH 4/4] docs: update docs with json emit mode --- README.md | 1 + src/bin/main.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 142e1f1d272..7a97d31bab9 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,7 @@ needs to be specified in `rustfmt.toml`, e.g., with `edition = "2018"`. | stdout | writes output to stdout | No | | coverage | displays how much of the input file was processed | Yes | | checkstyle | emits in a checkstyle format | Yes | + | json | emits diffs in a json format | Yes | ## License diff --git a/src/bin/main.rs b/src/bin/main.rs index 1ebad122a31..9d3be60ac8f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -98,7 +98,7 @@ fn make_opts() -> Options { ); let is_nightly = is_nightly(); let emit_opts = if is_nightly { - "[files|stdout|coverage|checkstyle]" + "[files|stdout|coverage|checkstyle|json]" } else { "[files|stdout]" };