]> git.lizzy.rs Git - rust.git/blob - src/config.rs
Merge pull request #1511 from topecongiro/bug/closure-fallback
[rust.git] / src / config.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 extern crate toml;
12
13 use file_lines::FileLines;
14 use lists::{SeparatorTactic, ListTactic};
15
16 macro_rules! configuration_option_enum{
17     ($e:ident: $( $x:ident ),+ $(,)*) => {
18         #[derive(Copy, Clone, Eq, PartialEq, Debug)]
19         pub enum $e {
20             $( $x ),+
21         }
22
23         impl_enum_decodable!($e, $( $x ),+);
24     }
25 }
26
27 configuration_option_enum! { Style:
28     Rfc, // Follow the style RFCs style.
29     Default, // Follow the traditional Rustfmt style.
30 }
31
32 configuration_option_enum! { NewlineStyle:
33     Windows, // \r\n
34     Unix, // \n
35     Native, // \r\n in Windows, \n on other platforms
36 }
37
38 configuration_option_enum! { BraceStyle:
39     AlwaysNextLine,
40     PreferSameLine,
41     // Prefer same line except where there is a where clause, in which case force
42     // the brace to the next line.
43     SameLineWhere,
44 }
45
46 configuration_option_enum! { ControlBraceStyle:
47     // K&R style, Rust community default
48     AlwaysSameLine,
49     // Stroustrup style
50     ClosingNextLine,
51     // Allman style
52     AlwaysNextLine,
53 }
54
55 // How to indent a function's return type.
56 configuration_option_enum! { ReturnIndent:
57     // Aligned with the arguments
58     WithArgs,
59     // Aligned with the where clause
60     WithWhereClause,
61 }
62
63 configuration_option_enum! { IndentStyle:
64     // First line on the same line as the opening brace, all lines aligned with
65     // the first line.
66     Visual,
67     // First line is on a new line and all lines align with block indent.
68     Block,
69 }
70
71 configuration_option_enum! { Density:
72     // Fit as much on one line as possible.
73     Compressed,
74     // Use more lines.
75     Tall,
76     // Try to compress if the body is empty.
77     CompressedIfEmpty,
78     // Place every item on a separate line.
79     Vertical,
80 }
81
82 configuration_option_enum! { TypeDensity:
83     // No spaces around "=" and "+"
84     Compressed,
85     // Spaces around " = " and " + "
86     Wide,
87 }
88
89
90 impl Density {
91     pub fn to_list_tactic(self) -> ListTactic {
92         match self {
93             Density::Compressed => ListTactic::Mixed,
94             Density::Tall |
95             Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
96             Density::Vertical => ListTactic::Vertical,
97         }
98     }
99 }
100
101 configuration_option_enum! { LicensePolicy:
102     // Do not place license text at top of files
103     NoLicense,
104     // Use the text in "license" field as the license
105     TextLicense,
106     // Use a text file as the license text
107     FileLicense,
108 }
109
110 configuration_option_enum! { MultilineStyle:
111     // Use horizontal layout if it fits in one line, fall back to vertical
112     PreferSingle,
113     // Use vertical layout
114     ForceMulti,
115 }
116
117 impl MultilineStyle {
118     pub fn to_list_tactic(self) -> ListTactic {
119         match self {
120             MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
121             MultilineStyle::ForceMulti => ListTactic::Vertical,
122         }
123     }
124 }
125
126 configuration_option_enum! { ReportTactic:
127     Always,
128     Unnumbered,
129     Never,
130 }
131
132 configuration_option_enum! { WriteMode:
133     // Backs the original file up and overwrites the original.
134     Replace,
135     // Overwrites original file without backup.
136     Overwrite,
137     // Writes the output to stdout.
138     Display,
139     // Writes the diff to stdout.
140     Diff,
141     // Displays how much of the input file was processed
142     Coverage,
143     // Unfancy stdout
144     Plain,
145     // Outputs a checkstyle XML file.
146     Checkstyle,
147 }
148
149 /// Trait for types that can be used in `Config`.
150 pub trait ConfigType: Sized {
151     /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
152     /// pipe-separated list of variants; for other types it returns "<type>".
153     fn doc_hint() -> String;
154 }
155
156 impl ConfigType for bool {
157     fn doc_hint() -> String {
158         String::from("<boolean>")
159     }
160 }
161
162 impl ConfigType for usize {
163     fn doc_hint() -> String {
164         String::from("<unsigned integer>")
165     }
166 }
167
168 impl ConfigType for isize {
169     fn doc_hint() -> String {
170         String::from("<signed integer>")
171     }
172 }
173
174 impl ConfigType for String {
175     fn doc_hint() -> String {
176         String::from("<string>")
177     }
178 }
179
180 impl ConfigType for FileLines {
181     fn doc_hint() -> String {
182         String::from("<json>")
183     }
184 }
185
186 pub struct ConfigHelpItem {
187     option_name: &'static str,
188     doc_string: &'static str,
189     variant_names: String,
190     default: &'static str,
191 }
192
193 impl ConfigHelpItem {
194     pub fn option_name(&self) -> &'static str {
195         self.option_name
196     }
197
198     pub fn doc_string(&self) -> &'static str {
199         self.doc_string
200     }
201
202     pub fn variant_names(&self) -> &String {
203         &self.variant_names
204     }
205
206     pub fn default(&self) -> &'static str {
207         self.default
208     }
209 }
210
211 macro_rules! create_config {
212     ($($i:ident: $ty:ty, $def:expr, $( $dstring:expr ),+ );+ $(;)*) => (
213         #[derive(Deserialize, Clone)]
214         pub struct Config {
215             $(pub $i: $ty),+
216         }
217
218         // Just like the Config struct but with each property wrapped
219         // as Option<T>. This is used to parse a rustfmt.toml that doesn't
220         // specity all properties of `Config`.
221         // We first parse into `ParsedConfig`, then create a default `Config`
222         // and overwrite the properties with corresponding values from `ParsedConfig`
223         #[derive(Deserialize, Clone)]
224         pub struct ParsedConfig {
225             $(pub $i: Option<$ty>),+
226         }
227
228         impl Config {
229
230             fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
231             $(
232                 if let Some(val) = parsed.$i {
233                     self.$i = val;
234                 }
235             )+
236                 self
237             }
238
239             pub fn from_toml(toml: &str) -> Result<Config, String> {
240                 let parsed: toml::Value =
241                     toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
242                 let mut err: String = String::new();
243                 {
244                     let table = parsed
245                         .as_table()
246                         .ok_or(String::from("Parsed config was not table"))?;
247                     for (key, _) in table {
248                         match &**key {
249                             $(
250                                 stringify!($i) => (),
251                             )+
252                                 _ => {
253                                     let msg =
254                                         &format!("Warning: Unknown configuration option `{}`\n",
255                                                  key);
256                                     err.push_str(msg)
257                                 }
258                         }
259                     }
260                 }
261                 match parsed.try_into() {
262                     Ok(parsed_config) =>
263                         Ok(Config::default().fill_from_parsed_config(parsed_config)),
264                     Err(_) => {
265                         err.push_str("Error: Decoding config file failed. ");
266                         err.push_str("Please check your config file.\n");
267                         Err(err)
268                     }
269                 }
270             }
271
272             pub fn override_value(&mut self, key: &str, val: &str) {
273                 match key {
274                     $(
275                         stringify!($i) => {
276                             self.$i = val.parse::<$ty>()
277                                 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
278                                                  stringify!($i),
279                                                  val,
280                                                  stringify!($ty)));
281                         }
282                     )+
283                     _ => panic!("Unknown config key in override: {}", key)
284                 }
285             }
286
287             pub fn print_docs() {
288                 use std::cmp;
289                 let max = 0;
290                 $( let max = cmp::max(max, stringify!($i).len()+1); )+
291                 let mut space_str = String::with_capacity(max);
292                 for _ in 0..max {
293                     space_str.push(' ');
294                 }
295                 println!("Configuration Options:");
296                 $(
297                     let name_raw = stringify!($i);
298                     let mut name_out = String::with_capacity(max);
299                     for _ in name_raw.len()..max-1 {
300                         name_out.push(' ')
301                     }
302                     name_out.push_str(name_raw);
303                     name_out.push(' ');
304                     println!("{}{} Default: {:?}",
305                              name_out,
306                              <$ty>::doc_hint(),
307                              $def);
308                     $(
309                         println!("{}{}", space_str, $dstring);
310                     )+
311                     println!("");
312                 )+
313             }
314         }
315
316         // Template for the default configuration
317         impl Default for Config {
318             fn default() -> Config {
319                 Config {
320                     $(
321                         $i: $def,
322                     )+
323                 }
324             }
325         }
326     )
327 }
328
329 create_config! {
330     verbose: bool, false, "Use verbose output";
331     disable_all_formatting: bool, false, "Don't reformat anything";
332     skip_children: bool, false, "Don't reformat out of line modules";
333     file_lines: FileLines, FileLines::all(),
334         "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
335          via the --file-lines option";
336     max_width: usize, 100, "Maximum width of each line";
337     error_on_line_overflow: bool, true, "Error if unable to get all lines within max_width";
338     tab_spaces: usize, 4, "Number of spaces per tab";
339     fn_call_width: usize, 60,
340         "Maximum width of the args of a function call before falling back to vertical formatting";
341     struct_lit_width: usize, 18,
342         "Maximum width in the body of a struct lit before falling back to vertical formatting";
343     struct_variant_width: usize, 35,
344         "Maximum width in the body of a struct variant before falling back to vertical formatting";
345     force_explicit_abi: bool, true, "Always print the abi for extern items";
346     newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
347     fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
348     item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
349     control_style: Style, Style::Default, "Indent style for control flow statements";
350     control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
351         "Brace style for control flow constructs";
352     impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
353     trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
354         "How to handle trailing commas for lists";
355     fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
356     fn_single_line: bool, false, "Put single-expression functions on a single line";
357     fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
358         "Location of return type in function declaration";
359     fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
360     fn_args_density: Density, Density::Tall, "Argument density in functions";
361     fn_args_layout: IndentStyle, IndentStyle::Visual,
362         "Layout of function arguments and tuple structs";
363     array_layout: IndentStyle, IndentStyle::Visual, "Indent on arrays";
364     array_width: usize, 60,
365         "Maximum width of an array literal before falling back to vertical formatting";
366     type_punctuation_density: TypeDensity, TypeDensity::Wide,
367         "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
368     where_style: Style, Style::Default, "Overall strategy for where clauses";
369     // TODO:
370     // 1. Should we at least try to put the where clause on the same line as the rest of the
371     // function decl?
372     // 2. Currently options `Tall` and `Vertical` produce the same output.
373     where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
374     where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
375     where_pred_indent: IndentStyle, IndentStyle::Visual,
376         "Indentation style of a where predicate";
377     generics_indent: IndentStyle, IndentStyle::Visual, "Indentation of generics";
378     struct_lit_style: IndentStyle, IndentStyle::Block, "Style of struct definition";
379     struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
380         "Multiline style on literal structs";
381     fn_call_style: IndentStyle, IndentStyle::Visual, "Indentation for function calls, etc.";
382     report_todo: ReportTactic, ReportTactic::Never,
383         "Report all, none or unnumbered occurrences of TODO in source file comments";
384     report_fixme: ReportTactic, ReportTactic::Never,
385         "Report all, none or unnumbered occurrences of FIXME in source file comments";
386     chain_indent: IndentStyle, IndentStyle::Block, "Indentation of chain";
387     chain_one_line_max: usize, 60, "Maximum length of a chain to fit on a single line";
388     reorder_imports: bool, false, "Reorder import statements alphabetically";
389     reorder_imported_names: bool, false,
390         "Reorder lists of names in import statements alphabetically";
391     single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
392                                                 expressions. A value of zero means always break \
393                                                 if-else expressions.";
394     format_strings: bool, false, "Format string literals where necessary";
395     force_format_strings: bool, false, "Always format string literals";
396     take_source_hints: bool, false, "Retain some formatting characteristics from the source code";
397     hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
398     wrap_comments: bool, false, "Break comments to fit on the line";
399     comment_width: usize, 80, "Maximum length of comments. No effect unless wrap_comments = true";
400     normalize_comments: bool, false, "Convert /* */ comments to // comments where possible";
401     wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
402     match_block_trailing_comma: bool, false,
403         "Put a trailing comma after a block based match arm (non-block arms are not affected)";
404     indent_match_arms: bool, true, "Indent match arms instead of keeping them at the same \
405                                     indentation level as the match keyword";
406     closure_block_indent_threshold: isize, 7, "How many lines a closure must have before it is \
407                                                block indented. -1 means never use block indent.";
408     space_before_type_annotation: bool, false,
409         "Leave a space before the colon in a type annotation";
410     space_after_type_annotation_colon: bool, true,
411         "Leave a space after the colon in a type annotation";
412     space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
413     space_after_bound_colon: bool, true,
414         "Leave a space after the colon in a trait or lifetime bound";
415     spaces_around_ranges: bool, false, "Put spaces around the  .. and ... range operators";
416     spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
417     spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
418     spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
419     use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
420     write_mode: WriteMode, WriteMode::Replace,
421         "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
422     condense_wildcard_suffices: bool, false, "Replace strings of _ wildcards by a single .. in \
423                                               tuple patterns"
424 }