]> git.lizzy.rs Git - rust.git/blob - src/config.rs
Merge pull request #1562 from flier/reorder_imports_in_group
[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(e) => {
265                         err.push_str("Error: Decoding config file failed:\n");
266                         err.push_str(format!("{}\n", e).as_str());
267                         err.push_str("Please check your config file.\n");
268                         Err(err)
269                     }
270                 }
271             }
272
273             pub fn override_value(&mut self, key: &str, val: &str) {
274                 match key {
275                     $(
276                         stringify!($i) => {
277                             self.$i = val.parse::<$ty>()
278                                 .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
279                                                  stringify!($i),
280                                                  val,
281                                                  stringify!($ty)));
282                         }
283                     )+
284                     _ => panic!("Unknown config key in override: {}", key)
285                 }
286             }
287
288             pub fn print_docs() {
289                 use std::cmp;
290                 let max = 0;
291                 $( let max = cmp::max(max, stringify!($i).len()+1); )+
292                 let mut space_str = String::with_capacity(max);
293                 for _ in 0..max {
294                     space_str.push(' ');
295                 }
296                 println!("Configuration Options:");
297                 $(
298                     let name_raw = stringify!($i);
299                     let mut name_out = String::with_capacity(max);
300                     for _ in name_raw.len()..max-1 {
301                         name_out.push(' ')
302                     }
303                     name_out.push_str(name_raw);
304                     name_out.push(' ');
305                     println!("{}{} Default: {:?}",
306                              name_out,
307                              <$ty>::doc_hint(),
308                              $def);
309                     $(
310                         println!("{}{}", space_str, $dstring);
311                     )+
312                     println!("");
313                 )+
314             }
315         }
316
317         // Template for the default configuration
318         impl Default for Config {
319             fn default() -> Config {
320                 Config {
321                     $(
322                         $i: $def,
323                     )+
324                 }
325             }
326         }
327     )
328 }
329
330 create_config! {
331     verbose: bool, false, "Use verbose output";
332     disable_all_formatting: bool, false, "Don't reformat anything";
333     skip_children: bool, false, "Don't reformat out of line modules";
334     file_lines: FileLines, FileLines::all(),
335         "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
336          via the --file-lines option";
337     max_width: usize, 100, "Maximum width of each line";
338     error_on_line_overflow: bool, true, "Error if unable to get all lines within max_width";
339     tab_spaces: usize, 4, "Number of spaces per tab";
340     fn_call_width: usize, 60,
341         "Maximum width of the args of a function call before falling back to vertical formatting";
342     struct_lit_width: usize, 18,
343         "Maximum width in the body of a struct lit before falling back to vertical formatting";
344     struct_variant_width: usize, 35,
345         "Maximum width in the body of a struct variant before falling back to vertical formatting";
346     force_explicit_abi: bool, true, "Always print the abi for extern items";
347     newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
348     fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
349     item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
350     control_style: Style, Style::Default, "Indent style for control flow statements";
351     control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
352         "Brace style for control flow constructs";
353     impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
354     trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
355         "How to handle trailing commas for lists";
356     fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
357     fn_single_line: bool, false, "Put single-expression functions on a single line";
358     fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
359         "Location of return type in function declaration";
360     fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
361     fn_args_density: Density, Density::Tall, "Argument density in functions";
362     fn_args_layout: IndentStyle, IndentStyle::Visual,
363         "Layout of function arguments and tuple structs";
364     array_layout: IndentStyle, IndentStyle::Visual, "Indent on arrays";
365     array_width: usize, 60,
366         "Maximum width of an array literal before falling back to vertical formatting";
367     type_punctuation_density: TypeDensity, TypeDensity::Wide,
368         "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
369     where_style: Style, Style::Default, "Overall strategy for where clauses";
370     // TODO:
371     // 1. Should we at least try to put the where clause on the same line as the rest of the
372     // function decl?
373     // 2. Currently options `Tall` and `Vertical` produce the same output.
374     where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
375     where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
376     where_pred_indent: IndentStyle, IndentStyle::Visual,
377         "Indentation style of a where predicate";
378     generics_indent: IndentStyle, IndentStyle::Visual, "Indentation of generics";
379     struct_lit_style: IndentStyle, IndentStyle::Block, "Style of struct definition";
380     struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
381         "Multiline style on literal structs";
382     fn_call_style: IndentStyle, IndentStyle::Visual, "Indentation for function calls, etc.";
383     report_todo: ReportTactic, ReportTactic::Never,
384         "Report all, none or unnumbered occurrences of TODO in source file comments";
385     report_fixme: ReportTactic, ReportTactic::Never,
386         "Report all, none or unnumbered occurrences of FIXME in source file comments";
387     chain_indent: IndentStyle, IndentStyle::Block, "Indentation of chain";
388     chain_one_line_max: usize, 60, "Maximum length of a chain to fit on a single line";
389     reorder_imports: bool, false, "Reorder import statements alphabetically";
390     reorder_imports_in_group: bool, false, "Reorder import statements in group";
391     reorder_imported_names: bool, false,
392         "Reorder lists of names in import statements alphabetically";
393     single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
394                                                 expressions. A value of zero means always break \
395                                                 if-else expressions.";
396     format_strings: bool, false, "Format string literals where necessary";
397     force_format_strings: bool, false, "Always format string literals";
398     take_source_hints: bool, false, "Retain some formatting characteristics from the source code";
399     hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
400     wrap_comments: bool, false, "Break comments to fit on the line";
401     comment_width: usize, 80, "Maximum length of comments. No effect unless wrap_comments = true";
402     normalize_comments: bool, false, "Convert /* */ comments to // comments where possible";
403     wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
404     match_block_trailing_comma: bool, false,
405         "Put a trailing comma after a block based match arm (non-block arms are not affected)";
406     indent_match_arms: bool, true, "Indent match arms instead of keeping them at the same \
407                                     indentation level as the match keyword";
408     closure_block_indent_threshold: isize, 7, "How many lines a closure must have before it is \
409                                                block indented. -1 means never use block indent.";
410     space_before_type_annotation: bool, false,
411         "Leave a space before the colon in a type annotation";
412     space_after_type_annotation_colon: bool, true,
413         "Leave a space after the colon in a type annotation";
414     space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
415     space_after_bound_colon: bool, true,
416         "Leave a space after the colon in a trait or lifetime bound";
417     spaces_around_ranges: bool, false, "Put spaces around the  .. and ... range operators";
418     spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
419     spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
420     spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
421     use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
422     write_mode: WriteMode, WriteMode::Replace,
423         "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
424     condense_wildcard_suffices: bool, false, "Replace strings of _ wildcards by a single .. in \
425                                               tuple patterns"
426 }