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