compiletest/
compute_diff.rs1use std::collections::VecDeque;
2use std::fs::{File, FileType};
3
4use camino::Utf8Path;
5
6#[derive(Debug, PartialEq)]
7pub enum DiffLine {
8 Context(String),
9 Expected(String),
10 Resulting(String),
11}
12
13#[derive(Debug, PartialEq)]
14pub struct Mismatch {
15 pub line_number: u32,
16 pub lines: Vec<DiffLine>,
17}
18
19impl Mismatch {
20 fn new(line_number: u32) -> Mismatch {
21 Mismatch { line_number, lines: Vec::new() }
22 }
23}
24
25pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
27 let mut line_number = 1;
28 let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
29 let mut lines_since_mismatch = context_size + 1;
30 let mut results = Vec::new();
31 let mut mismatch = Mismatch::new(0);
32
33 for result in diff::lines(expected, actual) {
34 match result {
35 diff::Result::Left(s) => {
36 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
37 results.push(mismatch);
38 mismatch = Mismatch::new(line_number - context_queue.len() as u32);
39 }
40
41 while let Some(line) = context_queue.pop_front() {
42 mismatch.lines.push(DiffLine::Context(line.to_owned()));
43 }
44
45 mismatch.lines.push(DiffLine::Expected(s.to_owned()));
46 line_number += 1;
47 lines_since_mismatch = 0;
48 }
49 diff::Result::Right(s) => {
50 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
51 results.push(mismatch);
52 mismatch = Mismatch::new(line_number - context_queue.len() as u32);
53 }
54
55 while let Some(line) = context_queue.pop_front() {
56 mismatch.lines.push(DiffLine::Context(line.to_owned()));
57 }
58
59 mismatch.lines.push(DiffLine::Resulting(s.to_owned()));
60 lines_since_mismatch = 0;
61 }
62 diff::Result::Both(s, _) => {
63 if context_queue.len() >= context_size {
64 let _ = context_queue.pop_front();
65 }
66
67 if lines_since_mismatch < context_size {
68 mismatch.lines.push(DiffLine::Context(s.to_owned()));
69 } else if context_size > 0 {
70 context_queue.push_back(s);
71 }
72
73 line_number += 1;
74 lines_since_mismatch += 1;
75 }
76 }
77 }
78
79 results.push(mismatch);
80 results.remove(0);
81
82 results
83}
84
85pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
86 use std::fmt::Write;
87 let mut output = String::new();
88 let diff_results = make_diff(expected, actual, context_size);
89 for result in diff_results {
90 let mut line_number = result.line_number;
91 for line in result.lines {
92 match line {
93 DiffLine::Expected(e) => {
94 writeln!(output, "-\t{}", e).unwrap();
95 line_number += 1;
96 }
97 DiffLine::Context(c) => {
98 writeln!(output, "{}\t{}", line_number, c).unwrap();
99 line_number += 1;
100 }
101 DiffLine::Resulting(r) => {
102 writeln!(output, "+\t{}", r).unwrap();
103 }
104 }
105 }
106 writeln!(output).unwrap();
107 }
108 output
109}
110
111pub(crate) fn write_filtered_diff<Filter>(
115 diff_filename: &str,
116 out_dir: &Utf8Path,
117 compare_dir: &Utf8Path,
118 verbose: bool,
119 filter: Filter,
120) -> bool
121where
122 Filter: Fn(FileType, Option<&str>) -> bool,
123{
124 use std::io::{Read, Write};
125 let mut diff_output = File::create(diff_filename).unwrap();
126 let mut wrote_data = false;
127 for entry in walkdir::WalkDir::new(out_dir.as_std_path()) {
128 let entry = entry.expect("failed to read file");
129 let extension = entry.path().extension().and_then(|p| p.to_str());
130 if filter(entry.file_type(), extension) {
131 let expected_path = compare_dir
132 .as_std_path()
133 .join(entry.path().strip_prefix(&out_dir.as_std_path()).unwrap());
134 let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
135 let actual_path = entry.path();
136 let actual = std::fs::read(&actual_path).unwrap();
137 let diff = unified_diff::diff(
138 &expected,
139 &expected_path.to_str().unwrap(),
140 &actual,
141 &actual_path.to_str().unwrap(),
142 3,
143 );
144 wrote_data |= !diff.is_empty();
145 diff_output.write_all(&diff).unwrap();
146 }
147 }
148
149 if !wrote_data {
150 println!("note: diff is identical to nightly rustdoc");
151 assert!(diff_output.metadata().unwrap().len() == 0);
152 return false;
153 } else if verbose {
154 eprintln!("printing diff:");
155 let mut buf = Vec::new();
156 diff_output.read_to_end(&mut buf).unwrap();
157 std::io::stderr().lock().write_all(&mut buf).unwrap();
158 }
159 true
160}