]> git.lizzy.rs Git - rust.git/blob - src/config/options.rs
Use our own `FileName` struct rather than exporting libsyntax's
[rust.git] / src / config / options.rs
1 // Copyright 2018 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 config::config_type::ConfigType;
12 use config::lists::*;
13 use config::{Config, FileName};
14
15 use std::collections::HashSet;
16 use std::path::{Path, PathBuf};
17
18 /// Macro for deriving implementations of Serialize/Deserialize for enums
19 #[macro_export]
20 macro_rules! impl_enum_serialize_and_deserialize {
21     ( $e:ident, $( $x:ident ),* ) => {
22         impl ::serde::ser::Serialize for $e {
23             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
24                 where S: ::serde::ser::Serializer
25             {
26                 use serde::ser::Error;
27
28                 // We don't know whether the user of the macro has given us all options.
29                 #[allow(unreachable_patterns)]
30                 match *self {
31                     $(
32                         $e::$x => serializer.serialize_str(stringify!($x)),
33                     )*
34                     _ => {
35                         Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
36                     }
37                 }
38             }
39         }
40
41         impl<'de> ::serde::de::Deserialize<'de> for $e {
42             fn deserialize<D>(d: D) -> Result<Self, D::Error>
43                     where D: ::serde::Deserializer<'de> {
44                 use serde::de::{Error, Visitor};
45                 use std::marker::PhantomData;
46                 use std::fmt;
47                 struct StringOnly<T>(PhantomData<T>);
48                 impl<'de, T> Visitor<'de> for StringOnly<T>
49                         where T: ::serde::Deserializer<'de> {
50                     type Value = String;
51                     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
52                         formatter.write_str("string")
53                     }
54                     fn visit_str<E>(self, value: &str) -> Result<String, E> {
55                         Ok(String::from(value))
56                     }
57                 }
58                 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
59                 $(
60                     if stringify!($x).eq_ignore_ascii_case(&s) {
61                       return Ok($e::$x);
62                     }
63                 )*
64                 static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
65                 Err(D::Error::unknown_variant(&s, ALLOWED))
66             }
67         }
68
69         impl ::std::str::FromStr for $e {
70             type Err = &'static str;
71
72             fn from_str(s: &str) -> Result<Self, Self::Err> {
73                 $(
74                     if stringify!($x).eq_ignore_ascii_case(s) {
75                         return Ok($e::$x);
76                     }
77                 )*
78                 Err("Bad variant")
79             }
80         }
81
82         impl ConfigType for $e {
83             fn doc_hint() -> String {
84                 let mut variants = Vec::new();
85                 $(
86                     variants.push(stringify!($x));
87                 )*
88                 format!("[{}]", variants.join("|"))
89             }
90         }
91     };
92 }
93
94 macro_rules! configuration_option_enum{
95     ($e:ident: $( $x:ident ),+ $(,)*) => {
96         #[derive(Copy, Clone, Eq, PartialEq, Debug)]
97         pub enum $e {
98             $( $x ),+
99         }
100
101         impl_enum_serialize_and_deserialize!($e, $( $x ),+);
102     }
103 }
104
105 configuration_option_enum! { NewlineStyle:
106     Windows, // \r\n
107     Unix, // \n
108     Native, // \r\n in Windows, \n on other platforms
109 }
110
111 configuration_option_enum! { BraceStyle:
112     AlwaysNextLine,
113     PreferSameLine,
114     // Prefer same line except where there is a where clause, in which case force
115     // the brace to the next line.
116     SameLineWhere,
117 }
118
119 configuration_option_enum! { ControlBraceStyle:
120     // K&R style, Rust community default
121     AlwaysSameLine,
122     // Stroustrup style
123     ClosingNextLine,
124     // Allman style
125     AlwaysNextLine,
126 }
127
128 configuration_option_enum! { IndentStyle:
129     // First line on the same line as the opening brace, all lines aligned with
130     // the first line.
131     Visual,
132     // First line is on a new line and all lines align with block indent.
133     Block,
134 }
135
136 configuration_option_enum! { Density:
137     // Fit as much on one line as possible.
138     Compressed,
139     // Use more lines.
140     Tall,
141     // Place every item on a separate line.
142     Vertical,
143 }
144
145 configuration_option_enum! { TypeDensity:
146     // No spaces around "=" and "+"
147     Compressed,
148     // Spaces around " = " and " + "
149     Wide,
150 }
151
152 impl Density {
153     pub fn to_list_tactic(self) -> ListTactic {
154         match self {
155             Density::Compressed => ListTactic::Mixed,
156             Density::Tall => ListTactic::HorizontalVertical,
157             Density::Vertical => ListTactic::Vertical,
158         }
159     }
160 }
161
162 configuration_option_enum! { ReportTactic:
163     Always,
164     Unnumbered,
165     Never,
166 }
167
168 configuration_option_enum! { WriteMode:
169     // Overwrites original file without backup.
170     Overwrite,
171     // Backs the original file up and overwrites the original.
172     Replace,
173     // Writes the output to stdout.
174     Display,
175     // Displays how much of the input file was processed
176     Coverage,
177     // Unfancy stdout
178     Checkstyle,
179     // Output the changed lines (for internal value only)
180     Modified,
181     // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
182     // This option is designed to be run in CI where a non-zero exit signifies non-standard code
183     // formatting.
184     Check,
185     // Rustfmt shouldn't output anything formatting-like (e.g., emit a help message).
186     None,
187 }
188
189 configuration_option_enum! { Color:
190     // Always use color, whether it is a piped or terminal output
191     Always,
192     // Never use color
193     Never,
194     // Automatically use color, if supported by terminal
195     Auto,
196 }
197
198 configuration_option_enum! { Verbosity:
199     // Emit more.
200     Verbose,
201     Normal,
202     // Emit as little as possible.
203     Quiet,
204 }
205
206 #[derive(Deserialize, Serialize, Clone, Debug)]
207 pub struct WidthHeuristics {
208     // Maximum width of the args of a function call before falling back
209     // to vertical formatting.
210     pub fn_call_width: usize,
211     // Maximum width in the body of a struct lit before falling back to
212     // vertical formatting.
213     pub struct_lit_width: usize,
214     // Maximum width in the body of a struct variant before falling back
215     // to vertical formatting.
216     pub struct_variant_width: usize,
217     // Maximum width of an array literal before falling back to vertical
218     // formatting.
219     pub array_width: usize,
220     // Maximum length of a chain to fit on a single line.
221     pub chain_width: usize,
222     // Maximum line length for single line if-else expressions. A value
223     // of zero means always break if-else expressions.
224     pub single_line_if_else_max_width: usize,
225 }
226
227 impl WidthHeuristics {
228     // Using this WidthHeuristics means we ignore heuristics.
229     pub fn null() -> WidthHeuristics {
230         WidthHeuristics {
231             fn_call_width: usize::max_value(),
232             struct_lit_width: 0,
233             struct_variant_width: 0,
234             array_width: usize::max_value(),
235             chain_width: usize::max_value(),
236             single_line_if_else_max_width: 0,
237         }
238     }
239
240     // scale the default WidthHeuristics according to max_width
241     pub fn scaled(max_width: usize) -> WidthHeuristics {
242         const DEFAULT_MAX_WIDTH: usize = 100;
243         let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
244             let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
245             // round to the closest 0.1
246             (ratio * 10.0).round() / 10.0
247         } else {
248             1.0
249         };
250         WidthHeuristics {
251             fn_call_width: (60.0 * max_width_ratio).round() as usize,
252             struct_lit_width: (18.0 * max_width_ratio).round() as usize,
253             struct_variant_width: (35.0 * max_width_ratio).round() as usize,
254             array_width: (60.0 * max_width_ratio).round() as usize,
255             chain_width: (60.0 * max_width_ratio).round() as usize,
256             single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
257         }
258     }
259 }
260
261 impl ::std::str::FromStr for WidthHeuristics {
262     type Err = &'static str;
263
264     fn from_str(_: &str) -> Result<Self, Self::Err> {
265         Err("WidthHeuristics is not parsable")
266     }
267 }
268
269 impl Default for WriteMode {
270     fn default() -> WriteMode {
271         WriteMode::Overwrite
272     }
273 }
274
275 /// A set of directories, files and modules that rustfmt should ignore.
276 #[derive(Default, Deserialize, Serialize, Clone, Debug)]
277 pub struct IgnoreList(HashSet<PathBuf>);
278
279 impl IgnoreList {
280     pub fn add_prefix(&mut self, dir: &Path) {
281         self.0 = self
282             .0
283             .iter()
284             .map(|s| {
285                 if s.has_root() {
286                     s.clone()
287                 } else {
288                     let mut path = PathBuf::from(dir);
289                     path.push(s);
290                     path
291                 }
292             })
293             .collect();
294     }
295
296     fn skip_file_inner(&self, file: &Path) -> bool {
297         self.0.iter().any(|path| file.starts_with(path))
298     }
299
300     pub fn skip_file(&self, file: &FileName) -> bool {
301         if let FileName::Real(ref path) = file {
302             self.skip_file_inner(path)
303         } else {
304             false
305         }
306     }
307 }
308
309 impl ::std::str::FromStr for IgnoreList {
310     type Err = &'static str;
311
312     fn from_str(_: &str) -> Result<Self, Self::Err> {
313         Err("IgnoreList is not parsable")
314     }
315 }
316
317 pub trait CliOptions {
318     fn apply_to(self, config: &mut Config);
319     fn config_path(&self) -> Option<&Path>;
320 }