compiletest/
compute_diff.rs
1use std::collections::VecDeque;
2use std::fs::{File, FileType};
3use std::path::Path;
4
5#[derive(Debug, PartialEq)]
6pub enum DiffLine {
7 Context(String),
8 Expected(String),
9 Resulting(String),
10}
11
12#[derive(Debug, PartialEq)]
13pub struct Mismatch {
14 pub line_number: u32,
15 pub lines: Vec<DiffLine>,
16}
17
18impl Mismatch {
19 fn new(line_number: u32) -> Mismatch {
20 Mismatch { line_number, lines: Vec::new() }
21 }
22}
23
24pub 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);
31
32 for result in diff::lines(expected, actual) {
33 match result {
34 diff::Result::Left(s) => {
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);
38 }
39
40 while let Some(line) = context_queue.pop_front() {
41 mismatch.lines.push(DiffLine::Context(line.to_owned()));
42 }
43
44 mismatch.lines.push(DiffLine::Expected(s.to_owned()));
45 line_number += 1;
46 lines_since_mismatch = 0;
47 }
48 diff::Result::Right(s) => {
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);
52 }
53
54 while let Some(line) = context_queue.pop_front() {
55 mismatch.lines.push(DiffLine::Context(line.to_owned()));
56 }
57
58 mismatch.lines.push(DiffLine::Resulting(s.to_owned()));
59 lines_since_mismatch = 0;
60 }
61 diff::Result::Both(s, _) => {
62 if context_queue.len() >= context_size {
63 let _ = context_queue.pop_front();
64 }
65
66 if lines_since_mismatch < context_size {
67 mismatch.lines.push(DiffLine::Context(s.to_owned()));
68 } else if context_size > 0 {
69 context_queue.push_back(s);
70 }
71
72 line_number += 1;
73 lines_since_mismatch += 1;
74 }
75 }
76 }
77
78 results.push(mismatch);
79 results.remove(0);
80
81 results
82}
83
84pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
85 use std::fmt::Write;
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 {
91 match line {
92 DiffLine::Expected(e) => {
93 writeln!(output, "-\t{}", e).unwrap();
94 line_number += 1;
95 }
96 DiffLine::Context(c) => {
97 writeln!(output, "{}\t{}", line_number, c).unwrap();
98 line_number += 1;
99 }
100 DiffLine::Resulting(r) => {
101 writeln!(output, "+\t{}", r).unwrap();
102 }
103 }
104 }
105 writeln!(output).unwrap();
106 }
107 output
108}
109
110pub(crate) fn write_filtered_diff<Filter>(
114 diff_filename: &str,
115 out_dir: &Path,
116 compare_dir: &Path,
117 verbose: bool,
118 filter: Filter,
119) -> bool
120where
121 Filter: Fn(FileType, Option<&str>) -> bool,
122{
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(
135 &expected,
136 &expected_path.to_string_lossy(),
137 &actual,
138 &actual_path.to_string_lossy(),
139 3,
140 );
141 wrote_data |= !diff.is_empty();
142 diff_output.write_all(&diff).unwrap();
143 }
144 }
145
146 if !wrote_data {
147 println!("note: diff is identical to nightly rustdoc");
148 assert!(diff_output.metadata().unwrap().len() == 0);
149 return false;
150 } else if verbose {
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();
155 }
156 true
157}