compiletest/runtest/
compute_diff.rs

1use std::collections::VecDeque;
2
3#[derive(Debug, PartialEq)]
4pub enum DiffLine {
5    Context(String),
6    Expected(String),
7    Resulting(String),
8}
9
10#[derive(Debug, PartialEq)]
11pub struct Mismatch {
12    pub line_number: u32,
13    pub lines: Vec<DiffLine>,
14}
15
16impl Mismatch {
17    fn new(line_number: u32) -> Mismatch {
18        Mismatch { line_number, lines: Vec::new() }
19    }
20}
21
22// Produces a diff between the expected output and actual output.
23pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
24    let mut line_number = 1;
25    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
26    let mut lines_since_mismatch = context_size + 1;
27    let mut results = Vec::new();
28    let mut mismatch = Mismatch::new(0);
29
30    for result in diff::lines(expected, actual) {
31        match result {
32            diff::Result::Left(s) => {
33                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
34                    results.push(mismatch);
35                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
36                }
37
38                while let Some(line) = context_queue.pop_front() {
39                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
40                }
41
42                mismatch.lines.push(DiffLine::Expected(s.to_owned()));
43                line_number += 1;
44                lines_since_mismatch = 0;
45            }
46            diff::Result::Right(s) => {
47                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
48                    results.push(mismatch);
49                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
50                }
51
52                while let Some(line) = context_queue.pop_front() {
53                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
54                }
55
56                mismatch.lines.push(DiffLine::Resulting(s.to_owned()));
57                lines_since_mismatch = 0;
58            }
59            diff::Result::Both(s, _) => {
60                if context_queue.len() >= context_size {
61                    let _ = context_queue.pop_front();
62                }
63
64                if lines_since_mismatch < context_size {
65                    mismatch.lines.push(DiffLine::Context(s.to_owned()));
66                } else if context_size > 0 {
67                    context_queue.push_back(s);
68                }
69
70                line_number += 1;
71                lines_since_mismatch += 1;
72            }
73        }
74    }
75
76    results.push(mismatch);
77    results.remove(0);
78
79    results
80}
81
82pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
83    use std::fmt::Write;
84    let mut output = String::new();
85    let diff_results = make_diff(expected, actual, context_size);
86    for result in diff_results {
87        let mut line_number = result.line_number;
88        for line in result.lines {
89            match line {
90                DiffLine::Expected(e) => {
91                    writeln!(output, "-\t{}", e).unwrap();
92                    line_number += 1;
93                }
94                DiffLine::Context(c) => {
95                    writeln!(output, "{}\t{}", line_number, c).unwrap();
96                    line_number += 1;
97                }
98                DiffLine::Resulting(r) => {
99                    writeln!(output, "+\t{}", r).unwrap();
100                }
101            }
102        }
103        writeln!(output).unwrap();
104    }
105    output
106}