]> git.lizzy.rs Git - rust.git/blob - src/filemap.rs
Merge branch 'master' of https://github.com/rust-lang-nursery/rustfmt into config
[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 strings::string_buffer::StringBuffer;
15
16 use std::fs::{self, File};
17 use std::io::{self, Write, Read, BufWriter};
18
19 use config::{NewlineStyle, Config, WriteMode};
20 use rustfmt_diff::{make_diff, print_diff, Mismatch};
21 use checkstyle::{output_header, output_footer, output_checkstyle_file};
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>(file_map: &FileMap, out: &mut T, config: &Config) -> Result<(), io::Error>
34     where T: Write
35 {
36     output_header(out, config.write_mode()).ok();
37     for &(ref filename, ref text) in file_map {
38         write_file(text, filename, out, config)?;
39     }
40     output_footer(out, config.write_mode()).ok();
41
42     Ok(())
43 }
44
45 // Prints all newlines either as `\n` or as `\r\n`.
46 pub fn write_system_newlines<T>(writer: T,
47                                 text: &StringBuffer,
48                                 config: &Config)
49                                 -> Result<(), io::Error>
50     where T: Write
51 {
52     // Buffer output, since we're writing a since char at a time.
53     let mut writer = BufWriter::new(writer);
54
55     let style = if config.newline_style() == NewlineStyle::Native {
56         if cfg!(windows) {
57             NewlineStyle::Windows
58         } else {
59             NewlineStyle::Unix
60         }
61     } else {
62         config.newline_style()
63     };
64
65     match style {
66         NewlineStyle::Unix => write!(writer, "{}", text),
67         NewlineStyle::Windows => {
68             for (c, _) in text.chars() {
69                 match c {
70                     '\n' => write!(writer, "\r\n")?,
71                     '\r' => continue,
72                     c => write!(writer, "{}", c)?,
73                 }
74             }
75             Ok(())
76         }
77         NewlineStyle::Native => unreachable!(),
78     }
79 }
80
81 pub fn write_file<T>(text: &StringBuffer,
82                      filename: &str,
83                      out: &mut T,
84                      config: &Config)
85                      -> Result<bool, io::Error>
86     where T: Write
87 {
88
89     fn source_and_formatted_text(text: &StringBuffer,
90                                  filename: &str,
91                                  config: &Config)
92                                  -> Result<(String, String), io::Error> {
93         let mut f = File::open(filename)?;
94         let mut ori_text = String::new();
95         f.read_to_string(&mut ori_text)?;
96         let mut v = Vec::new();
97         write_system_newlines(&mut v, text, config)?;
98         let fmt_text = String::from_utf8(v).unwrap();
99         Ok((ori_text, fmt_text))
100     }
101
102     fn create_diff(filename: &str,
103                    text: &StringBuffer,
104                    config: &Config)
105                    -> Result<Vec<Mismatch>, io::Error> {
106         let (ori, fmt) = source_and_formatted_text(text, filename, config)?;
107         Ok(make_diff(&ori, &fmt, 3))
108     }
109
110     match config.write_mode() {
111         WriteMode::Replace => {
112             if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
113                 if fmt != ori {
114                     // Do a little dance to make writing safer - write to a temp file
115                     // rename the original to a .bk, then rename the temp file to the
116                     // original.
117                     let tmp_name = filename.to_owned() + ".tmp";
118                     let bk_name = filename.to_owned() + ".bk";
119                     {
120                         // Write text to temp file
121                         let tmp_file = File::create(&tmp_name)?;
122                         write_system_newlines(tmp_file, text, config)?;
123                     }
124
125                     fs::rename(filename, bk_name)?;
126                     fs::rename(tmp_name, filename)?;
127                 }
128             }
129         }
130         WriteMode::Overwrite => {
131             // Write text directly over original file.
132             let file = File::create(filename)?;
133             write_system_newlines(file, text, config)?;
134         }
135         WriteMode::Plain => {
136             write_system_newlines(out, text, config)?;
137         }
138         WriteMode::Display | WriteMode::Coverage => {
139             println!("{}:\n", filename);
140             write_system_newlines(out, text, config)?;
141         }
142         WriteMode::Diff => {
143             if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
144                 let mismatch = make_diff(&ori, &fmt, 3);
145                 let has_diff = !mismatch.is_empty();
146                 print_diff(mismatch,
147                            |line_num| format!("Diff in {} at line {}:", filename, line_num));
148                 return Ok(has_diff);
149             }
150         }
151         WriteMode::Checkstyle => {
152             let diff = create_diff(filename, text, config)?;
153             output_checkstyle_file(out, filename, diff)?;
154         }
155     }
156
157     // when we are not in diff mode, don't indicate differing files
158     Ok(false)
159 }