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