1 use std::collections::VecDeque;
2 use std::fs::{File, FileType};
5 #[derive(Debug, PartialEq)]
12 #[derive(Debug, PartialEq)]
15 pub lines: Vec<DiffLine>,
19 fn new(line_number: u32) -> Mismatch {
20 Mismatch { line_number, lines: Vec::new() }
24 // Produces a diff between the expected output and actual output.
25 pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
26 let mut line_number = 1;
27 let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
28 let mut lines_since_mismatch = context_size + 1;
29 let mut results = Vec::new();
30 let mut mismatch = Mismatch::new(0);
32 for result in diff::lines(expected, actual) {
34 diff::Result::Left(str) => {
35 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
36 results.push(mismatch);
37 mismatch = Mismatch::new(line_number - context_queue.len() as u32);
40 while let Some(line) = context_queue.pop_front() {
41 mismatch.lines.push(DiffLine::Context(line.to_owned()));
44 mismatch.lines.push(DiffLine::Expected(str.to_owned()));
46 lines_since_mismatch = 0;
48 diff::Result::Right(str) => {
49 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
50 results.push(mismatch);
51 mismatch = Mismatch::new(line_number - context_queue.len() as u32);
54 while let Some(line) = context_queue.pop_front() {
55 mismatch.lines.push(DiffLine::Context(line.to_owned()));
58 mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
59 lines_since_mismatch = 0;
61 diff::Result::Both(str, _) => {
62 if context_queue.len() >= context_size {
63 let _ = context_queue.pop_front();
66 if lines_since_mismatch < context_size {
67 mismatch.lines.push(DiffLine::Context(str.to_owned()));
68 } else if context_size > 0 {
69 context_queue.push_back(str);
73 lines_since_mismatch += 1;
78 results.push(mismatch);
84 pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
86 let mut output = String::new();
87 let diff_results = make_diff(expected, actual, context_size);
88 for result in diff_results {
89 let mut line_number = result.line_number;
90 for line in result.lines {
92 DiffLine::Expected(e) => {
93 writeln!(output, "-\t{}", e).unwrap();
96 DiffLine::Context(c) => {
97 writeln!(output, "{}\t{}", line_number, c).unwrap();
100 DiffLine::Resulting(r) => {
101 writeln!(output, "+\t{}", r).unwrap();
105 writeln!(output).unwrap();
110 /// Filters based on filetype and extension whether to diff a file.
112 /// Returns whether any data was actually written.
113 pub(crate) fn write_filtered_diff<Filter>(
121 Filter: Fn(FileType, Option<&str>) -> bool,
123 use std::io::{Read, Write};
124 let mut diff_output = File::create(diff_filename).unwrap();
125 let mut wrote_data = false;
126 for entry in walkdir::WalkDir::new(out_dir) {
127 let entry = entry.expect("failed to read file");
128 let extension = entry.path().extension().and_then(|p| p.to_str());
129 if filter(entry.file_type(), extension) {
130 let expected_path = compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap());
131 let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
132 let actual_path = entry.path();
133 let actual = std::fs::read(&actual_path).unwrap();
134 let diff = unified_diff::diff(
136 &expected_path.to_string_lossy(),
138 &actual_path.to_string_lossy(),
141 wrote_data |= !diff.is_empty();
142 diff_output.write_all(&diff).unwrap();
147 println!("note: diff is identical to nightly rustdoc");
148 assert!(diff_output.metadata().unwrap().len() == 0);
151 eprintln!("printing diff:");
152 let mut buf = Vec::new();
153 diff_output.read_to_end(&mut buf).unwrap();
154 std::io::stderr().lock().write_all(&mut buf).unwrap();