]> git.lizzy.rs Git - rust.git/blob - src/config/options.rs
Extract applying of newline style into own mod
[rust.git] / src / config / options.rs
1 use std::collections::{hash_set, HashSet};
2 use std::fmt;
3 use std::path::{Path, PathBuf};
4
5 use atty;
6 use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
7
8 use crate::config::config_type::ConfigType;
9 use crate::config::lists::*;
10 use crate::config::Config;
11
12 /// Macro that will stringify the enum variants or a provided textual repr
13 #[macro_export]
14 macro_rules! configuration_option_enum_stringify {
15     ($variant:ident) => {
16         stringify!($variant)
17     };
18
19     ($_variant:ident: $value:expr) => {
20         stringify!($value)
21     };
22 }
23
24 /// Macro for deriving implementations of Serialize/Deserialize for enums
25 #[macro_export]
26 macro_rules! impl_enum_serialize_and_deserialize {
27     ( $e:ident, $( $variant:ident $(: $value:expr)* ),* ) => {
28         impl ::serde::ser::Serialize for $e {
29             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
30                 where S: ::serde::ser::Serializer
31             {
32                 use serde::ser::Error;
33
34                 // We don't know whether the user of the macro has given us all options.
35                 #[allow(unreachable_patterns)]
36                 match *self {
37                     $(
38                         $e::$variant => serializer.serialize_str(
39                             configuration_option_enum_stringify!($variant $(: $value)*)
40                         ),
41                     )*
42                     _ => {
43                         Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
44                     }
45                 }
46             }
47         }
48
49         impl<'de> ::serde::de::Deserialize<'de> for $e {
50             fn deserialize<D>(d: D) -> Result<Self, D::Error>
51                     where D: ::serde::Deserializer<'de> {
52                 use serde::de::{Error, Visitor};
53                 use std::marker::PhantomData;
54                 use std::fmt;
55                 struct StringOnly<T>(PhantomData<T>);
56                 impl<'de, T> Visitor<'de> for StringOnly<T>
57                         where T: ::serde::Deserializer<'de> {
58                     type Value = String;
59                     fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60                         formatter.write_str("string")
61                     }
62                     fn visit_str<E>(self, value: &str) -> Result<String, E> {
63                         Ok(String::from(value))
64                     }
65                 }
66                 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
67                 $(
68                     if configuration_option_enum_stringify!($variant $(: $value)*)
69                         .eq_ignore_ascii_case(&s) {
70                       return Ok($e::$variant);
71                     }
72                 )*
73                 static ALLOWED: &'static[&str] = &[
74                     $(configuration_option_enum_stringify!($variant $(: $value)*),)*];
75                 Err(D::Error::unknown_variant(&s, ALLOWED))
76             }
77         }
78
79         impl ::std::str::FromStr for $e {
80             type Err = &'static str;
81
82             fn from_str(s: &str) -> Result<Self, Self::Err> {
83                 $(
84                     if configuration_option_enum_stringify!($variant $(: $value)*)
85                         .eq_ignore_ascii_case(s) {
86                         return Ok($e::$variant);
87                     }
88                 )*
89                 Err("Bad variant")
90             }
91         }
92
93         impl ConfigType for $e {
94             fn doc_hint() -> String {
95                 let mut variants = Vec::new();
96                 $(
97                     variants.push(
98                         configuration_option_enum_stringify!($variant $(: $value)*)
99                     );
100                 )*
101                 format!("[{}]", variants.join("|"))
102             }
103         }
104     };
105 }
106
107 macro_rules! configuration_option_enum {
108     ($e:ident: $( $name:ident $(: $value:expr)* ),+ $(,)*) => (
109         #[derive(Copy, Clone, Eq, PartialEq)]
110         pub enum $e {
111             $( $name ),+
112         }
113
114         impl ::std::fmt::Debug for $e {
115             fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
116                 f.write_str(match self {
117                     $(
118                         $e::$name => configuration_option_enum_stringify!($name $(: $value)*),
119                     )+
120                 })
121             }
122         }
123
124         impl_enum_serialize_and_deserialize!($e, $( $name $(: $value)* ),+);
125     );
126 }
127
128 configuration_option_enum! { NewlineStyle:
129     Auto, // Auto-detect based on the raw source input
130     Windows, // \r\n
131     Unix, // \n
132     Native, // \r\n in Windows, \n on other platforms
133 }
134
135 configuration_option_enum! { BraceStyle:
136     AlwaysNextLine,
137     PreferSameLine,
138     // Prefer same line except where there is a where-clause, in which case force
139     // the brace to the next line.
140     SameLineWhere,
141 }
142
143 configuration_option_enum! { ControlBraceStyle:
144     // K&R style, Rust community default
145     AlwaysSameLine,
146     // Stroustrup style
147     ClosingNextLine,
148     // Allman style
149     AlwaysNextLine,
150 }
151
152 configuration_option_enum! { IndentStyle:
153     // First line on the same line as the opening brace, all lines aligned with
154     // the first line.
155     Visual,
156     // First line is on a new line and all lines align with block indent.
157     Block,
158 }
159
160 configuration_option_enum! { Density:
161     // Fit as much on one line as possible.
162     Compressed,
163     // Use more lines.
164     Tall,
165     // Place every item on a separate line.
166     Vertical,
167 }
168
169 configuration_option_enum! { TypeDensity:
170     // No spaces around "=" and "+"
171     Compressed,
172     // Spaces around " = " and " + "
173     Wide,
174 }
175
176 configuration_option_enum! { Heuristics:
177     // Turn off any heuristics
178     Off,
179     // Turn on max heuristics
180     Max,
181     // Use Rustfmt's defaults
182     Default,
183 }
184
185 impl Density {
186     pub fn to_list_tactic(self, len: usize) -> ListTactic {
187         match self {
188             Density::Compressed => ListTactic::Mixed,
189             Density::Tall => ListTactic::HorizontalVertical,
190             Density::Vertical if len == 1 => ListTactic::Horizontal,
191             Density::Vertical => ListTactic::Vertical,
192         }
193     }
194 }
195
196 configuration_option_enum! { ReportTactic:
197     Always,
198     Unnumbered,
199     Never,
200 }
201
202 // What Rustfmt should emit. Mostly corresponds to the `--emit` command line
203 // option.
204 configuration_option_enum! { EmitMode:
205     // Emits to files.
206     Files,
207     // Writes the output to stdout.
208     Stdout,
209     // Displays how much of the input file was processed
210     Coverage,
211     // Unfancy stdout
212     Checkstyle,
213     // Output the changed lines (for internal value only)
214     ModifiedLines,
215     // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
216     // This option is designed to be run in CI where a non-zero exit signifies non-standard code
217     // formatting. Used for `--check`.
218     Diff,
219 }
220
221 // Client-preference for coloured output.
222 configuration_option_enum! { Color:
223     // Always use color, whether it is a piped or terminal output
224     Always,
225     // Never use color
226     Never,
227     // Automatically use color, if supported by terminal
228     Auto,
229 }
230
231 configuration_option_enum! { Version:
232     // 1.x.y
233     One,
234     // 2.x.y
235     Two,
236 }
237
238 impl Color {
239     /// Whether we should use a coloured terminal.
240     pub fn use_colored_tty(self) -> bool {
241         match self {
242             Color::Always => true,
243             Color::Never => false,
244             Color::Auto => atty::is(atty::Stream::Stdout),
245         }
246     }
247 }
248
249 // How chatty should Rustfmt be?
250 configuration_option_enum! { Verbosity:
251     // Emit more.
252     Verbose,
253     Normal,
254     // Emit as little as possible.
255     Quiet,
256 }
257
258 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
259 pub struct WidthHeuristics {
260     // Maximum width of the args of a function call before falling back
261     // to vertical formatting.
262     pub fn_call_width: usize,
263     // Maximum width of the args of a function-like attributes before falling
264     // back to vertical formatting.
265     pub attr_fn_like_width: usize,
266     // Maximum width in the body of a struct lit before falling back to
267     // vertical formatting.
268     pub struct_lit_width: usize,
269     // Maximum width in the body of a struct variant before falling back
270     // to vertical formatting.
271     pub struct_variant_width: usize,
272     // Maximum width of an array literal before falling back to vertical
273     // formatting.
274     pub array_width: usize,
275     // Maximum length of a chain to fit on a single line.
276     pub chain_width: usize,
277     // Maximum line length for single line if-else expressions. A value
278     // of zero means always break if-else expressions.
279     pub single_line_if_else_max_width: usize,
280 }
281
282 impl WidthHeuristics {
283     // Using this WidthHeuristics means we ignore heuristics.
284     pub fn null() -> WidthHeuristics {
285         WidthHeuristics {
286             fn_call_width: usize::max_value(),
287             attr_fn_like_width: usize::max_value(),
288             struct_lit_width: 0,
289             struct_variant_width: 0,
290             array_width: usize::max_value(),
291             chain_width: usize::max_value(),
292             single_line_if_else_max_width: 0,
293         }
294     }
295
296     pub fn set(max_width: usize) -> WidthHeuristics {
297         WidthHeuristics {
298             fn_call_width: max_width,
299             attr_fn_like_width: max_width,
300             struct_lit_width: max_width,
301             struct_variant_width: max_width,
302             array_width: max_width,
303             chain_width: max_width,
304             single_line_if_else_max_width: max_width,
305         }
306     }
307
308     // scale the default WidthHeuristics according to max_width
309     pub fn scaled(max_width: usize) -> WidthHeuristics {
310         const DEFAULT_MAX_WIDTH: usize = 100;
311         let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
312             let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
313             // round to the closest 0.1
314             (ratio * 10.0).round() / 10.0
315         } else {
316             1.0
317         };
318         WidthHeuristics {
319             fn_call_width: (60.0 * max_width_ratio).round() as usize,
320             attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
321             struct_lit_width: (18.0 * max_width_ratio).round() as usize,
322             struct_variant_width: (35.0 * max_width_ratio).round() as usize,
323             array_width: (60.0 * max_width_ratio).round() as usize,
324             chain_width: (60.0 * max_width_ratio).round() as usize,
325             single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
326         }
327     }
328 }
329
330 impl ::std::str::FromStr for WidthHeuristics {
331     type Err = &'static str;
332
333     fn from_str(_: &str) -> Result<Self, Self::Err> {
334         Err("WidthHeuristics is not parsable")
335     }
336 }
337
338 impl Default for EmitMode {
339     fn default() -> EmitMode {
340         EmitMode::Files
341     }
342 }
343
344 /// A set of directories, files and modules that rustfmt should ignore.
345 #[derive(Default, Serialize, Clone, Debug, PartialEq)]
346 pub struct IgnoreList {
347     /// A set of path specified in rustfmt.toml.
348     #[serde(flatten)]
349     path_set: HashSet<PathBuf>,
350     /// A path to rustfmt.toml.
351     #[serde(skip_serializing)]
352     rustfmt_toml_path: PathBuf,
353 }
354
355 impl<'de> Deserialize<'de> for IgnoreList {
356     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
357     where
358         D: Deserializer<'de>,
359     {
360         struct HashSetVisitor;
361         impl<'v> Visitor<'v> for HashSetVisitor {
362             type Value = HashSet<PathBuf>;
363
364             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
365                 formatter.write_str("a sequence of path")
366             }
367
368             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
369             where
370                 A: SeqAccess<'v>,
371             {
372                 let mut path_set = HashSet::new();
373                 while let Some(elem) = seq.next_element()? {
374                     path_set.insert(elem);
375                 }
376                 Ok(path_set)
377             }
378         }
379         Ok(IgnoreList {
380             path_set: deserializer.deserialize_seq(HashSetVisitor)?,
381             rustfmt_toml_path: PathBuf::new(),
382         })
383     }
384 }
385
386 impl<'a> IntoIterator for &'a IgnoreList {
387     type Item = &'a PathBuf;
388     type IntoIter = hash_set::Iter<'a, PathBuf>;
389
390     fn into_iter(self) -> Self::IntoIter {
391         self.path_set.iter()
392     }
393 }
394
395 impl IgnoreList {
396     pub fn add_prefix(&mut self, dir: &Path) {
397         self.rustfmt_toml_path = dir.to_path_buf();
398     }
399
400     pub fn rustfmt_toml_path(&self) -> &Path {
401         &self.rustfmt_toml_path
402     }
403 }
404
405 impl ::std::str::FromStr for IgnoreList {
406     type Err = &'static str;
407
408     fn from_str(_: &str) -> Result<Self, Self::Err> {
409         Err("IgnoreList is not parsable")
410     }
411 }
412
413 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
414 /// values in a config with values from the command line.
415 pub trait CliOptions {
416     fn apply_to(self, config: &mut Config);
417     fn config_path(&self) -> Option<&Path>;
418 }
419
420 // The edition of the compiler (RFC 2052)
421 configuration_option_enum! { Edition:
422     Edition2015: 2015,
423     Edition2018: 2018,
424 }
425
426 impl Default for Edition {
427     fn default() -> Edition {
428         Edition::Edition2015
429     }
430 }
431
432 impl Edition {
433     pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
434         match self {
435             Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
436             Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
437         }
438     }
439 }