]> git.lizzy.rs Git - rust.git/blob - src/filemap.rs
Add a verbose-diff option
[rust.git] / src / filemap.rs
1 // Copyright 2015 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 // TODO: add tests
12
13 use std::fs::{self, File};
14 use std::io::{self, BufWriter, Read, Write};
15 use std::path::Path;
16
17 use checkstyle::{output_checkstyle_file, output_footer, output_header};
18 use config::{Config, NewlineStyle, WriteMode};
19 use rustfmt_diff::{make_diff, output_modified, print_diff, Mismatch};
20 use syntax::codemap::FileName;
21
22 use FileRecord;
23
24 // Append a newline to the end of each file.
25 pub fn append_newline(s: &mut String) {
26     s.push_str("\n");
27 }
28
29 pub fn write_all_files<T>(
30     file_map: &[FileRecord],
31     out: &mut T,
32     config: &Config,
33 ) -> Result<(), io::Error>
34 where
35     T: Write,
36 {
37     output_header(out, config.write_mode()).ok();
38     for &(ref filename, ref text) in file_map {
39         write_file(text, filename, out, config)?;
40     }
41     output_footer(out, config.write_mode()).ok();
42
43     Ok(())
44 }
45
46 // Prints all newlines either as `\n` or as `\r\n`.
47 pub fn write_system_newlines<T>(writer: T, text: &str, config: &Config) -> Result<(), io::Error>
48 where
49     T: Write,
50 {
51     // Buffer output, since we're writing a since char at a time.
52     let mut writer = BufWriter::new(writer);
53
54     let style = if config.newline_style() == NewlineStyle::Native {
55         if cfg!(windows) {
56             NewlineStyle::Windows
57         } else {
58             NewlineStyle::Unix
59         }
60     } else {
61         config.newline_style()
62     };
63
64     match style {
65         NewlineStyle::Unix => write!(writer, "{}", text),
66         NewlineStyle::Windows => {
67             for c in text.chars() {
68                 match c {
69                     '\n' => write!(writer, "\r\n")?,
70                     '\r' => continue,
71                     c => write!(writer, "{}", c)?,
72                 }
73             }
74             Ok(())
75         }
76         NewlineStyle::Native => unreachable!(),
77     }
78 }
79
80 pub fn write_file<T>(
81     text: &str,
82     filename: &FileName,
83     out: &mut T,
84     config: &Config,
85 ) -> Result<bool, io::Error>
86 where
87     T: Write,
88 {
89     fn source_and_formatted_text(
90         text: &str,
91         filename: &Path,
92         config: &Config,
93     ) -> Result<(String, String), io::Error> {
94         let mut f = File::open(filename)?;
95         let mut ori_text = String::new();
96         f.read_to_string(&mut ori_text)?;
97         let mut v = Vec::new();
98         write_system_newlines(&mut v, text, config)?;
99         let fmt_text = String::from_utf8(v).unwrap();
100         Ok((ori_text, fmt_text))
101     }
102
103     fn create_diff(
104         filename: &Path,
105         text: &str,
106         config: &Config,
107     ) -> Result<Vec<Mismatch>, io::Error> {
108         let (ori, fmt) = source_and_formatted_text(text, filename, config)?;
109         Ok(make_diff(&ori, &fmt, 3))
110     }
111
112     let filename_to_path = || match *filename {
113         FileName::Real(ref path) => path,
114         _ => panic!("cannot format `{}` with WriteMode::Replace", filename),
115     };
116
117     match config.write_mode() {
118         WriteMode::Replace => {
119             let filename = filename_to_path();
120             if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
121                 if fmt != ori {
122                     // Do a little dance to make writing safer - write to a temp file
123                     // rename the original to a .bk, then rename the temp file to the
124                     // original.
125                     let tmp_name = filename.with_extension("tmp");
126                     let bk_name = filename.with_extension("bk");
127                     {
128                         // Write text to temp file
129                         let tmp_file = File::create(&tmp_name)?;
130                         write_system_newlines(tmp_file, text, config)?;
131                     }
132
133                     fs::rename(filename, bk_name)?;
134                     fs::rename(tmp_name, filename)?;
135                 }
136             }
137         }
138         WriteMode::Overwrite => {
139             // Write text directly over original file if there is a diff.
140             let filename = filename_to_path();
141             let (source, formatted) = source_and_formatted_text(text, filename, config)?;
142             if source != formatted {
143                 let file = File::create(filename)?;
144                 write_system_newlines(file, text, config)?;
145             }
146         }
147         WriteMode::Plain => {
148             write_system_newlines(out, text, config)?;
149         }
150         WriteMode::Display | WriteMode::Coverage => {
151             println!("{}:\n", filename);
152             write_system_newlines(out, text, config)?;
153         }
154         WriteMode::Diff => {
155             let filename = filename_to_path();
156             if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
157                 let mismatch = make_diff(&ori, &fmt, 3);
158                 let has_diff = !mismatch.is_empty();
159                 print_diff(
160                     mismatch,
161                     |line_num| format!("Diff in {} at line {}:", filename.display(), line_num),
162                     config,
163                 );
164                 return Ok(has_diff);
165             }
166         }
167         WriteMode::Modified => {
168             let filename = filename_to_path();
169             if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
170                 let mismatch = make_diff(&ori, &fmt, 0);
171                 let has_diff = !mismatch.is_empty();
172                 output_modified(out, mismatch);
173                 return Ok(has_diff);
174             }
175         }
176         WriteMode::Checkstyle => {
177             let filename = filename_to_path();
178             let diff = create_diff(filename, text, config)?;
179             output_checkstyle_file(out, filename, diff)?;
180         }
181         WriteMode::Check => {
182             let filename = filename_to_path();
183             if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
184                 let mismatch = make_diff(&ori, &fmt, 3);
185                 let has_diff = !mismatch.is_empty();
186                 print_diff(
187                     mismatch,
188                     |line_num| format!("Diff in {} at line {}:", filename.display(), line_num),
189                     config,
190                 );
191                 return Ok(has_diff);
192             }
193         }
194         WriteMode::None => {}
195     }
196
197     // when we are not in diff mode, don't indicate differing files
198     Ok(false)
199 }