2 use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
4 use serde_json::to_string as to_json_string;
5 use std::io::{self, Write};
8 #[derive(Debug, Default)]
9 pub(crate) struct JsonEmitter {
13 #[derive(Debug, Default, Serialize)]
14 struct MismatchedBlock {
15 original_begin_line: u32,
16 original_end_line: u32,
17 expected_begin_line: u32,
18 expected_end_line: u32,
23 #[derive(Debug, Default, Serialize)]
24 struct MismatchedFile {
26 mismatches: Vec<MismatchedBlock>,
29 impl Emitter for JsonEmitter {
30 fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
35 fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
40 fn emit_formatted_file(
42 output: &mut dyn Write,
48 ) -> Result<EmitterResult, io::Error> {
49 const CONTEXT_SIZE: usize = 0;
50 let filename = ensure_real_path(filename);
51 let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
52 let has_diff = !diff.is_empty();
55 output_json_file(output, filename, diff, self.num_files)?;
59 Ok(EmitterResult { has_diff })
63 fn output_json_file<T>(
67 num_emitted_files: u32,
68 ) -> Result<(), io::Error>
72 let mut mismatches = vec![];
73 for mismatch in diff {
74 let original_begin_line = mismatch.line_number_orig;
75 let expected_begin_line = mismatch.line_number;
76 let mut original_end_line = original_begin_line;
77 let mut expected_end_line = expected_begin_line;
78 let mut original_line_counter = 0;
79 let mut expected_line_counter = 0;
80 let mut original_lines = vec![];
81 let mut expected_lines = vec![];
83 for line in mismatch.lines {
85 DiffLine::Expected(msg) => {
86 expected_end_line = expected_begin_line + expected_line_counter;
87 expected_line_counter += 1;
88 expected_lines.push(msg)
90 DiffLine::Resulting(msg) => {
91 original_end_line = original_begin_line + original_line_counter;
92 original_line_counter += 1;
93 original_lines.push(msg)
95 DiffLine::Context(_) => continue,
99 mismatches.push(MismatchedBlock {
104 original: original_lines.join("\n"),
105 expected: expected_lines.join("\n"),
108 let json = to_json_string(&MismatchedFile {
109 name: String::from(filename.to_str().unwrap()),
112 let prefix = if num_emitted_files > 0 { "," } else { "" };
113 write!(writer, "{}{}", prefix, &json)?;
121 use std::path::PathBuf;
124 fn expected_line_range_correct_when_single_line_split() {
125 let file = "foo/bar.rs";
126 let mismatched_file = MismatchedFile {
127 name: String::from(file),
128 mismatches: vec![MismatchedBlock {
129 original_begin_line: 79,
130 original_end_line: 79,
131 expected_begin_line: 79,
132 expected_end_line: 82,
133 original: String::from("fn Foo<T>() where T: Bar {"),
134 expected: String::from("fn Foo<T>()\nwhere\n T: Bar,\n{"),
137 let mismatch = Mismatch {
139 line_number_orig: 79,
141 DiffLine::Resulting(String::from("fn Foo<T>() where T: Bar {")),
142 DiffLine::Expected(String::from("fn Foo<T>()")),
143 DiffLine::Expected(String::from("where")),
144 DiffLine::Expected(String::from(" T: Bar,")),
145 DiffLine::Expected(String::from("{")),
149 let mut writer = Vec::new();
150 let exp_json = to_json_string(&mismatched_file).unwrap();
151 let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
152 assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
156 fn context_lines_ignored() {
157 let file = "src/lib.rs";
158 let mismatched_file = MismatchedFile {
159 name: String::from(file),
160 mismatches: vec![MismatchedBlock {
161 original_begin_line: 5,
162 original_end_line: 5,
163 expected_begin_line: 5,
164 expected_end_line: 5,
165 original: String::from(
166 "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {",
168 expected: String::from(
169 "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {",
173 let mismatch = Mismatch {
177 DiffLine::Context(String::new()),
178 DiffLine::Resulting(String::from(
179 "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {",
181 DiffLine::Context(String::new()),
182 DiffLine::Expected(String::from(
183 "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {",
185 DiffLine::Context(String::new()),
189 let mut writer = Vec::new();
190 let exp_json = to_json_string(&mismatched_file).unwrap();
191 let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
192 assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
196 fn emits_empty_array_on_no_diffs() {
197 let mut writer = Vec::new();
198 let mut emitter = JsonEmitter::default();
199 let _ = emitter.emit_header(&mut writer);
201 .emit_formatted_file(
204 filename: &FileName::Real(PathBuf::from("src/lib.rs")),
205 original_text: "fn empty() {}\n",
206 formatted_text: "fn empty() {}\n",
210 let _ = emitter.emit_footer(&mut writer);
211 assert_eq!(result.has_diff, false);
212 assert_eq!(&writer[..], "[]".as_bytes());
216 fn emits_array_with_files_with_diffs() {
217 let file_name = "src/bin.rs";
220 "println!(\"Hello, world!\");",
227 " assert_eq!(2 + 2, 4);",
231 let formatted = vec![
233 " println!(\"Hello, world!\");",
240 " assert_eq!(2 + 2, 4);",
244 let mut writer = Vec::new();
245 let mut emitter = JsonEmitter::default();
246 let _ = emitter.emit_header(&mut writer);
248 .emit_formatted_file(
251 filename: &FileName::Real(PathBuf::from(file_name)),
252 original_text: &original.join("\n"),
253 formatted_text: &formatted.join("\n"),
257 let _ = emitter.emit_footer(&mut writer);
258 let exp_json = to_json_string(&MismatchedFile {
259 name: String::from(file_name),
262 original_begin_line: 2,
263 original_end_line: 2,
264 expected_begin_line: 2,
265 expected_end_line: 2,
266 original: String::from("println!(\"Hello, world!\");"),
267 expected: String::from(" println!(\"Hello, world!\");"),
270 original_begin_line: 7,
271 original_end_line: 10,
272 expected_begin_line: 7,
273 expected_end_line: 10,
274 original: String::from(
275 "#[test]\nfn it_works() {\n assert_eq!(2 + 2, 4);\n}",
277 expected: String::from(
278 " #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }",
284 assert_eq!(result.has_diff, true);
285 assert_eq!(&writer[..], format!("[{}]", exp_json).as_bytes());
289 fn emits_valid_json_with_multiple_files() {
290 let bin_file = "src/bin.rs";
291 let bin_original = vec!["fn main() {", "println!(\"Hello, world!\");", "}"];
292 let bin_formatted = vec!["fn main() {", " println!(\"Hello, world!\");", "}"];
293 let lib_file = "src/lib.rs";
294 let lib_original = vec!["fn greet() {", "println!(\"Greetings!\");", "}"];
295 let lib_formatted = vec!["fn greet() {", " println!(\"Greetings!\");", "}"];
296 let mut writer = Vec::new();
297 let mut emitter = JsonEmitter::default();
298 let _ = emitter.emit_header(&mut writer);
300 .emit_formatted_file(
303 filename: &FileName::Real(PathBuf::from(bin_file)),
304 original_text: &bin_original.join("\n"),
305 formatted_text: &bin_formatted.join("\n"),
310 .emit_formatted_file(
313 filename: &FileName::Real(PathBuf::from(lib_file)),
314 original_text: &lib_original.join("\n"),
315 formatted_text: &lib_formatted.join("\n"),
319 let _ = emitter.emit_footer(&mut writer);
320 let exp_bin_json = to_json_string(&MismatchedFile {
321 name: String::from(bin_file),
322 mismatches: vec![MismatchedBlock {
323 original_begin_line: 2,
324 original_end_line: 2,
325 expected_begin_line: 2,
326 expected_end_line: 2,
327 original: String::from("println!(\"Hello, world!\");"),
328 expected: String::from(" println!(\"Hello, world!\");"),
332 let exp_lib_json = to_json_string(&MismatchedFile {
333 name: String::from(lib_file),
334 mismatches: vec![MismatchedBlock {
335 original_begin_line: 2,
336 original_end_line: 2,
337 expected_begin_line: 2,
338 expected_end_line: 2,
339 original: String::from("println!(\"Greetings!\");"),
340 expected: String::from(" println!(\"Greetings!\");"),
346 format!("[{},{}]", exp_bin_json, exp_lib_json).as_bytes()