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.
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.
11 use config::config_type::ConfigType;
13 use config::{Config, FileName};
15 use isatty::stdout_isatty;
17 use std::collections::HashSet;
18 use std::path::{Path, PathBuf};
20 /// Macro for deriving implementations of Serialize/Deserialize for enums
22 macro_rules! impl_enum_serialize_and_deserialize {
23 ( $e:ident, $( $x:ident ),* ) => {
24 impl ::serde::ser::Serialize for $e {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26 where S: ::serde::ser::Serializer
28 use serde::ser::Error;
30 // We don't know whether the user of the macro has given us all options.
31 #[allow(unreachable_patterns)]
34 $e::$x => serializer.serialize_str(stringify!($x)),
37 Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
43 impl<'de> ::serde::de::Deserialize<'de> for $e {
44 fn deserialize<D>(d: D) -> Result<Self, D::Error>
45 where D: ::serde::Deserializer<'de> {
46 use serde::de::{Error, Visitor};
47 use std::marker::PhantomData;
49 struct StringOnly<T>(PhantomData<T>);
50 impl<'de, T> Visitor<'de> for StringOnly<T>
51 where T: ::serde::Deserializer<'de> {
53 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54 formatter.write_str("string")
56 fn visit_str<E>(self, value: &str) -> Result<String, E> {
57 Ok(String::from(value))
60 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
62 if stringify!($x).eq_ignore_ascii_case(&s) {
66 static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
67 Err(D::Error::unknown_variant(&s, ALLOWED))
71 impl ::std::str::FromStr for $e {
72 type Err = &'static str;
74 fn from_str(s: &str) -> Result<Self, Self::Err> {
76 if stringify!($x).eq_ignore_ascii_case(s) {
84 impl ConfigType for $e {
85 fn doc_hint() -> String {
86 let mut variants = Vec::new();
88 variants.push(stringify!($x));
90 format!("[{}]", variants.join("|"))
96 macro_rules! configuration_option_enum{
97 ($e:ident: $( $x:ident ),+ $(,)*) => {
98 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
103 impl_enum_serialize_and_deserialize!($e, $( $x ),+);
107 configuration_option_enum! { NewlineStyle:
108 Auto, // Auto-detect based on the raw source input
111 Native, // \r\n in Windows, \n on other platforms
115 fn auto_detect(raw_input_text: &str) -> NewlineStyle {
116 if let Some(pos) = raw_input_text.find('\n') {
117 let pos = pos.saturating_sub(1);
118 if let Some('\r') = raw_input_text.chars().nth(pos) {
119 NewlineStyle::Windows
128 fn native() -> NewlineStyle {
130 NewlineStyle::Windows
136 /// Apply this newline style to the formatted text. When the style is set
137 /// to `Auto`, the `raw_input_text` is used to detect the existing line
140 /// If the style is set to `Auto` and `raw_input_text` contains no
141 /// newlines, the `Native` style will be used.
142 pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) {
144 let mut style = self;
146 style = Self::auto_detect(raw_input_text);
149 style = Self::native();
153 let mut transformed = String::with_capacity(2 * formatted_text.capacity());
154 for c in formatted_text.chars() {
156 '\n' => transformed.push_str("\r\n"),
158 c => transformed.push(c),
161 *formatted_text = transformed;
164 Native => unreachable!("NewlineStyle::Native"),
165 Auto => unreachable!("NewlineStyle::Auto"),
170 configuration_option_enum! { BraceStyle:
173 // Prefer same line except where there is a where clause, in which case force
174 // the brace to the next line.
178 configuration_option_enum! { ControlBraceStyle:
179 // K&R style, Rust community default
187 configuration_option_enum! { IndentStyle:
188 // First line on the same line as the opening brace, all lines aligned with
191 // First line is on a new line and all lines align with block indent.
195 configuration_option_enum! { Density:
196 // Fit as much on one line as possible.
200 // Place every item on a separate line.
204 configuration_option_enum! { TypeDensity:
205 // No spaces around "=" and "+"
207 // Spaces around " = " and " + "
211 configuration_option_enum! { Heuristics:
212 // Turn off any heuristics
214 // Turn on max heuristics
216 // Use Rustfmt's defaults
221 pub fn to_list_tactic(self) -> ListTactic {
223 Density::Compressed => ListTactic::Mixed,
224 Density::Tall => ListTactic::HorizontalVertical,
225 Density::Vertical => ListTactic::Vertical,
230 configuration_option_enum! { ReportTactic:
236 // What Rustfmt should emit. Mostly corresponds to the `--emit` command line
238 configuration_option_enum! { EmitMode:
241 // Writes the output to stdout.
243 // Displays how much of the input file was processed
247 // Output the changed lines (for internal value only)
249 // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
250 // This option is designed to be run in CI where a non-zero exit signifies non-standard code
251 // formatting. Used for `--check`.
255 // Client-preference for coloured output.
256 configuration_option_enum! { Color:
257 // Always use color, whether it is a piped or terminal output
261 // Automatically use color, if supported by terminal
266 /// Whether we should use a coloured terminal.
267 pub fn use_colored_tty(self) -> bool {
269 Color::Always => true,
270 Color::Never => false,
271 Color::Auto => stdout_isatty(),
276 // How chatty should Rustfmt be?
277 configuration_option_enum! { Verbosity:
281 // Emit as little as possible.
285 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
286 pub struct WidthHeuristics {
287 // Maximum width of the args of a function call before falling back
288 // to vertical formatting.
289 pub fn_call_width: usize,
290 // Maximum width in the body of a struct lit before falling back to
291 // vertical formatting.
292 pub struct_lit_width: usize,
293 // Maximum width in the body of a struct variant before falling back
294 // to vertical formatting.
295 pub struct_variant_width: usize,
296 // Maximum width of an array literal before falling back to vertical
298 pub array_width: usize,
299 // Maximum length of a chain to fit on a single line.
300 pub chain_width: usize,
301 // Maximum line length for single line if-else expressions. A value
302 // of zero means always break if-else expressions.
303 pub single_line_if_else_max_width: usize,
306 impl WidthHeuristics {
307 // Using this WidthHeuristics means we ignore heuristics.
308 pub fn null() -> WidthHeuristics {
310 fn_call_width: usize::max_value(),
312 struct_variant_width: 0,
313 array_width: usize::max_value(),
314 chain_width: usize::max_value(),
315 single_line_if_else_max_width: 0,
319 pub fn set(max_width: usize) -> WidthHeuristics {
321 fn_call_width: max_width,
322 struct_lit_width: max_width,
323 struct_variant_width: max_width,
324 array_width: max_width,
325 chain_width: max_width,
326 single_line_if_else_max_width: max_width,
330 // scale the default WidthHeuristics according to max_width
331 pub fn scaled(max_width: usize) -> WidthHeuristics {
332 const DEFAULT_MAX_WIDTH: usize = 100;
333 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
334 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
335 // round to the closest 0.1
336 (ratio * 10.0).round() / 10.0
341 fn_call_width: (60.0 * max_width_ratio).round() as usize,
342 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
343 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
344 array_width: (60.0 * max_width_ratio).round() as usize,
345 chain_width: (60.0 * max_width_ratio).round() as usize,
346 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
351 impl ::std::str::FromStr for WidthHeuristics {
352 type Err = &'static str;
354 fn from_str(_: &str) -> Result<Self, Self::Err> {
355 Err("WidthHeuristics is not parsable")
359 impl Default for EmitMode {
360 fn default() -> EmitMode {
365 /// A set of directories, files and modules that rustfmt should ignore.
366 #[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)]
367 pub struct IgnoreList(HashSet<PathBuf>);
370 pub fn add_prefix(&mut self, dir: &Path) {
378 let mut path = PathBuf::from(dir);
385 fn skip_file_inner(&self, file: &Path) -> bool {
386 self.0.iter().any(|path| file.starts_with(path))
389 pub fn skip_file(&self, file: &FileName) -> bool {
390 if let FileName::Real(ref path) = file {
391 self.skip_file_inner(path)
398 impl ::std::str::FromStr for IgnoreList {
399 type Err = &'static str;
401 fn from_str(_: &str) -> Result<Self, Self::Err> {
402 Err("IgnoreList is not parsable")
406 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
407 /// values in a config with values from the command line.
408 pub trait CliOptions {
409 fn apply_to(self, config: &mut Config);
410 fn config_path(&self) -> Option<&Path>;
413 /// The edition of the compiler (RFC 2052)
414 configuration_option_enum!{ Edition:
420 pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
422 Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
423 Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
429 fn test_newline_style_auto_detect() {
430 let lf = "One\nTwo\nThree";
431 let crlf = "One\r\nTwo\r\nThree";
432 let none = "One Two Three";
434 assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf));
435 assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf));
436 assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none));
440 fn test_newline_style_auto_apply() {
441 let auto = NewlineStyle::Auto;
443 let formatted_text = "One\nTwo\nThree";
444 let raw_input_text = "One\nTwo\nThree";
446 let mut out = String::from(formatted_text);
447 auto.apply(&mut out, raw_input_text);
448 assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
450 let formatted_text = "One\nTwo\nThree";
451 let raw_input_text = "One\r\nTwo\r\nThree";
453 let mut out = String::from(formatted_text);
454 auto.apply(&mut out, raw_input_text);
455 assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
459 let formatted_text = "One\nTwo\nThree";
460 let raw_input_text = "One Two Three";
462 let mut out = String::from(formatted_text);
463 auto.apply(&mut out, raw_input_text);
465 "One\nTwo\nThree", &out,
466 "auto-native-unix should detect 'lf'"
472 let formatted_text = "One\nTwo\nThree";
473 let raw_input_text = "One Two Three";
475 let mut out = String::from(formatted_text);
476 auto.apply(&mut out, raw_input_text);
478 "One\r\nTwo\r\nThree", &out,
479 "auto-native-windows should detect 'crlf'"