1 use std::collections::HashSet;
2 use std::path::{Path, PathBuf};
6 use crate::config::config_type::ConfigType;
7 use crate::config::lists::*;
8 use crate::config::{Config, FileName};
10 /// Macro that will stringify the enum variants or a provided textual repr
12 macro_rules! configuration_option_enum_stringify {
17 ($_variant:ident: $value:expr) => {
22 /// Macro for deriving implementations of Serialize/Deserialize for enums
24 macro_rules! impl_enum_serialize_and_deserialize {
25 ( $e:ident, $( $variant:ident $(: $value:expr)* ),* ) => {
26 impl ::serde::ser::Serialize for $e {
27 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28 where S: ::serde::ser::Serializer
30 use serde::ser::Error;
32 // We don't know whether the user of the macro has given us all options.
33 #[allow(unreachable_patterns)]
36 $e::$variant => serializer.serialize_str(
37 configuration_option_enum_stringify!($variant $(: $value)*)
41 Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
47 impl<'de> ::serde::de::Deserialize<'de> for $e {
48 fn deserialize<D>(d: D) -> Result<Self, D::Error>
49 where D: ::serde::Deserializer<'de> {
50 use serde::de::{Error, Visitor};
51 use std::marker::PhantomData;
53 struct StringOnly<T>(PhantomData<T>);
54 impl<'de, T> Visitor<'de> for StringOnly<T>
55 where T: ::serde::Deserializer<'de> {
57 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
58 formatter.write_str("string")
60 fn visit_str<E>(self, value: &str) -> Result<String, E> {
61 Ok(String::from(value))
64 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
66 if configuration_option_enum_stringify!($variant $(: $value)*)
67 .eq_ignore_ascii_case(&s) {
68 return Ok($e::$variant);
71 static ALLOWED: &'static[&str] = &[
72 $(configuration_option_enum_stringify!($variant $(: $value)*),)*];
73 Err(D::Error::unknown_variant(&s, ALLOWED))
77 impl ::std::str::FromStr for $e {
78 type Err = &'static str;
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 if configuration_option_enum_stringify!($variant $(: $value)*)
83 .eq_ignore_ascii_case(s) {
84 return Ok($e::$variant);
91 impl ConfigType for $e {
92 fn doc_hint() -> String {
93 let mut variants = Vec::new();
96 configuration_option_enum_stringify!($variant $(: $value)*)
99 format!("[{}]", variants.join("|"))
105 macro_rules! configuration_option_enum {
106 ($e:ident: $( $name:ident $(: $value:expr)* ),+ $(,)*) => (
107 #[derive(Copy, Clone, Eq, PartialEq)]
112 impl ::std::fmt::Debug for $e {
113 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
114 f.write_str(match self {
116 $e::$name => configuration_option_enum_stringify!($name $(: $value)*),
122 impl_enum_serialize_and_deserialize!($e, $( $name $(: $value)* ),+);
126 configuration_option_enum! { NewlineStyle:
127 Auto, // Auto-detect based on the raw source input
130 Native, // \r\n in Windows, \n on other platforms
134 fn auto_detect(raw_input_text: &str) -> NewlineStyle {
135 if let Some(pos) = raw_input_text.find('\n') {
136 let pos = pos.saturating_sub(1);
137 if let Some('\r') = raw_input_text.chars().nth(pos) {
138 NewlineStyle::Windows
147 fn native() -> NewlineStyle {
149 NewlineStyle::Windows
155 /// Apply this newline style to the formatted text. When the style is set
156 /// to `Auto`, the `raw_input_text` is used to detect the existing line
159 /// If the style is set to `Auto` and `raw_input_text` contains no
160 /// newlines, the `Native` style will be used.
161 pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) {
162 use crate::NewlineStyle::*;
163 let mut style = self;
165 style = Self::auto_detect(raw_input_text);
168 style = Self::native();
172 let mut transformed = String::with_capacity(2 * formatted_text.capacity());
173 for c in formatted_text.chars() {
175 '\n' => transformed.push_str("\r\n"),
177 c => transformed.push(c),
180 *formatted_text = transformed;
183 Native => unreachable!("NewlineStyle::Native"),
184 Auto => unreachable!("NewlineStyle::Auto"),
189 configuration_option_enum! { BraceStyle:
192 // Prefer same line except where there is a where-clause, in which case force
193 // the brace to the next line.
197 configuration_option_enum! { ControlBraceStyle:
198 // K&R style, Rust community default
206 configuration_option_enum! { IndentStyle:
207 // First line on the same line as the opening brace, all lines aligned with
210 // First line is on a new line and all lines align with block indent.
214 configuration_option_enum! { Density:
215 // Fit as much on one line as possible.
219 // Place every item on a separate line.
223 configuration_option_enum! { TypeDensity:
224 // No spaces around "=" and "+"
226 // Spaces around " = " and " + "
230 configuration_option_enum! { Heuristics:
231 // Turn off any heuristics
233 // Turn on max heuristics
235 // Use Rustfmt's defaults
240 pub fn to_list_tactic(self, len: usize) -> ListTactic {
242 Density::Compressed => ListTactic::Mixed,
243 Density::Tall => ListTactic::HorizontalVertical,
244 Density::Vertical if len == 1 => ListTactic::Horizontal,
245 Density::Vertical => ListTactic::Vertical,
250 configuration_option_enum! { ReportTactic:
256 // What Rustfmt should emit. Mostly corresponds to the `--emit` command line
258 configuration_option_enum! { EmitMode:
261 // Writes the output to stdout.
263 // Displays how much of the input file was processed
267 // Output the changed lines (for internal value only)
269 // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
270 // This option is designed to be run in CI where a non-zero exit signifies non-standard code
271 // formatting. Used for `--check`.
275 // Client-preference for coloured output.
276 configuration_option_enum! { Color:
277 // Always use color, whether it is a piped or terminal output
281 // Automatically use color, if supported by terminal
285 configuration_option_enum! { Version:
293 /// Whether we should use a coloured terminal.
294 pub fn use_colored_tty(self) -> bool {
296 Color::Always => true,
297 Color::Never => false,
298 Color::Auto => atty::is(atty::Stream::Stdout),
303 // How chatty should Rustfmt be?
304 configuration_option_enum! { Verbosity:
308 // Emit as little as possible.
312 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
313 pub struct WidthHeuristics {
314 // Maximum width of the args of a function call before falling back
315 // to vertical formatting.
316 pub fn_call_width: usize,
317 // Maximum width of the args of a function-like attributes before falling
318 // back to vertical formatting.
319 pub attr_fn_like_width: usize,
320 // Maximum width in the body of a struct lit before falling back to
321 // vertical formatting.
322 pub struct_lit_width: usize,
323 // Maximum width in the body of a struct variant before falling back
324 // to vertical formatting.
325 pub struct_variant_width: usize,
326 // Maximum width of an array literal before falling back to vertical
328 pub array_width: usize,
329 // Maximum length of a chain to fit on a single line.
330 pub chain_width: usize,
331 // Maximum line length for single line if-else expressions. A value
332 // of zero means always break if-else expressions.
333 pub single_line_if_else_max_width: usize,
336 impl WidthHeuristics {
337 // Using this WidthHeuristics means we ignore heuristics.
338 pub fn null() -> WidthHeuristics {
340 fn_call_width: usize::max_value(),
341 attr_fn_like_width: usize::max_value(),
343 struct_variant_width: 0,
344 array_width: usize::max_value(),
345 chain_width: usize::max_value(),
346 single_line_if_else_max_width: 0,
350 pub fn set(max_width: usize) -> WidthHeuristics {
352 fn_call_width: max_width,
353 attr_fn_like_width: max_width,
354 struct_lit_width: max_width,
355 struct_variant_width: max_width,
356 array_width: max_width,
357 chain_width: max_width,
358 single_line_if_else_max_width: max_width,
362 // scale the default WidthHeuristics according to max_width
363 pub fn scaled(max_width: usize) -> WidthHeuristics {
364 const DEFAULT_MAX_WIDTH: usize = 100;
365 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
366 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
367 // round to the closest 0.1
368 (ratio * 10.0).round() / 10.0
373 fn_call_width: (60.0 * max_width_ratio).round() as usize,
374 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
375 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
376 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
377 array_width: (60.0 * max_width_ratio).round() as usize,
378 chain_width: (60.0 * max_width_ratio).round() as usize,
379 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
384 impl ::std::str::FromStr for WidthHeuristics {
385 type Err = &'static str;
387 fn from_str(_: &str) -> Result<Self, Self::Err> {
388 Err("WidthHeuristics is not parsable")
392 impl Default for EmitMode {
393 fn default() -> EmitMode {
398 /// A set of directories, files and modules that rustfmt should ignore.
399 #[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)]
400 pub struct IgnoreList(HashSet<PathBuf>);
403 pub fn add_prefix(&mut self, dir: &Path) {
411 let mut path = PathBuf::from(dir);
419 fn skip_file_inner(&self, file: &Path) -> bool {
420 self.0.iter().any(|path| file.starts_with(path))
423 pub fn skip_file(&self, file: &FileName) -> bool {
424 if let FileName::Real(ref path) = file {
425 self.skip_file_inner(path)
432 impl ::std::str::FromStr for IgnoreList {
433 type Err = &'static str;
435 fn from_str(_: &str) -> Result<Self, Self::Err> {
436 Err("IgnoreList is not parsable")
440 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
441 /// values in a config with values from the command line.
442 pub trait CliOptions {
443 fn apply_to(self, config: &mut Config);
444 fn config_path(&self) -> Option<&Path>;
447 // The edition of the compiler (RFC 2052)
448 configuration_option_enum! { Edition:
453 impl Default for Edition {
454 fn default() -> Edition {
460 pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
462 Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
463 Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
469 fn test_newline_style_auto_detect() {
470 let lf = "One\nTwo\nThree";
471 let crlf = "One\r\nTwo\r\nThree";
472 let none = "One Two Three";
474 assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf));
475 assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf));
476 assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none));
480 fn test_newline_style_auto_apply() {
481 let auto = NewlineStyle::Auto;
483 let formatted_text = "One\nTwo\nThree";
484 let raw_input_text = "One\nTwo\nThree";
486 let mut out = String::from(formatted_text);
487 auto.apply(&mut out, raw_input_text);
488 assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
490 let formatted_text = "One\nTwo\nThree";
491 let raw_input_text = "One\r\nTwo\r\nThree";
493 let mut out = String::from(formatted_text);
494 auto.apply(&mut out, raw_input_text);
495 assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
499 let formatted_text = "One\nTwo\nThree";
500 let raw_input_text = "One Two Three";
502 let mut out = String::from(formatted_text);
503 auto.apply(&mut out, raw_input_text);
505 "One\nTwo\nThree", &out,
506 "auto-native-unix should detect 'lf'"
512 let formatted_text = "One\nTwo\nThree";
513 let raw_input_text = "One Two Three";
515 let mut out = String::from(formatted_text);
516 auto.apply(&mut out, raw_input_text);
518 "One\r\nTwo\r\nThree", &out,
519 "auto-native-windows should detect 'crlf'"