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