]> git.lizzy.rs Git - rust.git/blob - src/rustfmt_diff.rs
Add a failing test of zero context.
[rust.git] / src / rustfmt_diff.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use diff;
12 use std::collections::VecDeque;
13 use std::io;
14 use term;
15 use utils::isatty;
16
17 #[derive(Debug, PartialEq)]
18 pub enum DiffLine {
19     Context(String),
20     Expected(String),
21     Resulting(String),
22 }
23
24 #[derive(Debug, PartialEq)]
25 pub struct Mismatch {
26     pub line_number: u32,
27     pub lines: Vec<DiffLine>,
28 }
29
30 impl Mismatch {
31     fn new(line_number: u32) -> Mismatch {
32         Mismatch {
33             line_number: line_number,
34             lines: Vec::new(),
35         }
36     }
37 }
38
39 // Produces a diff between the expected output and actual output of rustfmt.
40 pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
41     let mut line_number = 1;
42     let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
43     let mut lines_since_mismatch = context_size + 1;
44     let mut results = Vec::new();
45     let mut mismatch = Mismatch::new(0);
46
47     for result in diff::lines(expected, actual) {
48         match result {
49             diff::Result::Left(str) => {
50                 if lines_since_mismatch >= context_size {
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(str.to_owned()));
60                 lines_since_mismatch = 0;
61             }
62             diff::Result::Right(str) => {
63                 if lines_since_mismatch >= context_size {
64                     results.push(mismatch);
65                     mismatch = Mismatch::new(line_number - context_queue.len() as u32);
66                 }
67
68                 while let Some(line) = context_queue.pop_front() {
69                     mismatch.lines.push(DiffLine::Context(line.to_owned()));
70                 }
71
72                 mismatch.lines.push(DiffLine::Expected(str.to_owned()));
73                 line_number += 1;
74                 lines_since_mismatch = 0;
75             }
76             diff::Result::Both(str, _) => {
77                 if context_queue.len() >= context_size {
78                     let _ = context_queue.pop_front();
79                 }
80
81                 if lines_since_mismatch < context_size {
82                     mismatch.lines.push(DiffLine::Context(str.to_owned()));
83                 } else {
84                     context_queue.push_back(str);
85                 }
86
87                 line_number += 1;
88                 lines_since_mismatch += 1;
89             }
90         }
91     }
92
93     results.push(mismatch);
94     results.remove(0);
95
96     results
97 }
98
99 pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F)
100 where
101     F: Fn(u32) -> String,
102 {
103     match term::stdout() {
104         Some(ref t) if isatty() && t.supports_color() => {
105             print_diff_fancy(diff, get_section_title, term::stdout().unwrap())
106         }
107         _ => print_diff_basic(diff, get_section_title),
108     }
109 }
110
111 fn print_diff_fancy<F>(
112     diff: Vec<Mismatch>,
113     get_section_title: F,
114     mut t: Box<term::Terminal<Output = io::Stdout>>,
115 ) where
116     F: Fn(u32) -> String,
117 {
118     for mismatch in diff {
119         let title = get_section_title(mismatch.line_number);
120         writeln!(t, "{}", title).unwrap();
121
122         for line in mismatch.lines {
123             match line {
124                 DiffLine::Context(ref str) => {
125                     t.reset().unwrap();
126                     writeln!(t, " {}⏎", str).unwrap();
127                 }
128                 DiffLine::Expected(ref str) => {
129                     t.fg(term::color::GREEN).unwrap();
130                     writeln!(t, "+{}⏎", str).unwrap();
131                 }
132                 DiffLine::Resulting(ref str) => {
133                     t.fg(term::color::RED).unwrap();
134                     writeln!(t, "-{}⏎", str).unwrap();
135                 }
136             }
137         }
138         t.reset().unwrap();
139     }
140 }
141
142 pub fn print_diff_basic<F>(diff: Vec<Mismatch>, get_section_title: F)
143 where
144     F: Fn(u32) -> String,
145 {
146     for mismatch in diff {
147         let title = get_section_title(mismatch.line_number);
148         println!("{}", title);
149
150         for line in mismatch.lines {
151             match line {
152                 DiffLine::Context(ref str) => {
153                     println!(" {}⏎", str);
154                 }
155                 DiffLine::Expected(ref str) => {
156                     println!("+{}⏎", str);
157                 }
158                 DiffLine::Resulting(ref str) => {
159                     println!("-{}⏎", str);
160                 }
161             }
162         }
163     }
164 }
165
166 #[cfg(test)]
167 mod test {
168     use super::{make_diff,Mismatch};
169     use super::DiffLine::*;
170
171     #[test]
172     fn diff_simple() {
173         let src = "one\ntwo\nthree\nfour\nfive\n";
174         let dest= "one\ntwo\ntrois\nfour\nfive\n";
175         let diff = make_diff(src, dest, 1);
176         assert_eq!(diff, vec![Mismatch { line_number: 2,
177                                          lines: vec![
178                                              Context("two".into()),
179                                              Resulting("three".into()),
180                                              Expected("trois".into()),
181                                              Context("four".into()),
182                                          ] }]);
183     }
184
185     #[test]
186     fn diff_zerocontext() {
187         let src = "one\ntwo\nthree\nfour\nfive\n";
188         let dest= "one\ntwo\ntrois\nfour\nfive\n";
189         let diff = make_diff(src, dest, 0);
190         assert_eq!(diff, vec![Mismatch { line_number: 3,
191                                          lines: vec![
192                                              Resulting("three".into()),
193                                              Expected("trois".into()),
194                                          ] }]);
195     }
196 }