1 use std::collections::{hash_set, HashSet};
3 use std::path::{Path, PathBuf};
6 use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
8 use crate::config::config_type::ConfigType;
9 use crate::config::lists::*;
10 use crate::config::Config;
12 /// Macro that will stringify the enum variants or a provided textual repr
14 macro_rules! configuration_option_enum_stringify {
19 ($_variant:ident: $value:expr) => {
24 /// Macro for deriving implementations of Serialize/Deserialize for enums
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
32 use serde::ser::Error;
34 // We don't know whether the user of the macro has given us all options.
35 #[allow(unreachable_patterns)]
38 $e::$variant => serializer.serialize_str(
39 configuration_option_enum_stringify!($variant $(: $value)*)
43 Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
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;
55 struct StringOnly<T>(PhantomData<T>);
56 impl<'de, T> Visitor<'de> for StringOnly<T>
57 where T: ::serde::Deserializer<'de> {
59 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60 formatter.write_str("string")
62 fn visit_str<E>(self, value: &str) -> Result<String, E> {
63 Ok(String::from(value))
66 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
68 if configuration_option_enum_stringify!($variant $(: $value)*)
69 .eq_ignore_ascii_case(&s) {
70 return Ok($e::$variant);
73 static ALLOWED: &'static[&str] = &[
74 $(configuration_option_enum_stringify!($variant $(: $value)*),)*];
75 Err(D::Error::unknown_variant(&s, ALLOWED))
79 impl ::std::str::FromStr for $e {
80 type Err = &'static str;
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 if configuration_option_enum_stringify!($variant $(: $value)*)
85 .eq_ignore_ascii_case(s) {
86 return Ok($e::$variant);
93 impl ConfigType for $e {
94 fn doc_hint() -> String {
95 let mut variants = Vec::new();
98 configuration_option_enum_stringify!($variant $(: $value)*)
101 format!("[{}]", variants.join("|"))
107 macro_rules! configuration_option_enum {
108 ($e:ident: $( $name:ident $(: $value:expr)* ),+ $(,)*) => (
109 #[derive(Copy, Clone, Eq, PartialEq)]
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 {
118 $e::$name => configuration_option_enum_stringify!($name $(: $value)*),
124 impl_enum_serialize_and_deserialize!($e, $( $name $(: $value)* ),+);
128 configuration_option_enum! { NewlineStyle:
129 Auto, // Auto-detect based on the raw source input
132 Native, // \r\n in Windows, \n on other platforms
135 configuration_option_enum! { BraceStyle:
138 // Prefer same line except where there is a where-clause, in which case force
139 // the brace to the next line.
143 configuration_option_enum! { ControlBraceStyle:
144 // K&R style, Rust community default
152 configuration_option_enum! { IndentStyle:
153 // First line on the same line as the opening brace, all lines aligned with
156 // First line is on a new line and all lines align with block indent.
160 configuration_option_enum! { Density:
161 // Fit as much on one line as possible.
165 // Place every item on a separate line.
169 configuration_option_enum! { TypeDensity:
170 // No spaces around "=" and "+"
172 // Spaces around " = " and " + "
176 configuration_option_enum! { Heuristics:
177 // Turn off any heuristics
179 // Turn on max heuristics
181 // Use Rustfmt's defaults
186 pub fn to_list_tactic(self, len: usize) -> ListTactic {
188 Density::Compressed => ListTactic::Mixed,
189 Density::Tall => ListTactic::HorizontalVertical,
190 Density::Vertical if len == 1 => ListTactic::Horizontal,
191 Density::Vertical => ListTactic::Vertical,
196 configuration_option_enum! { ReportTactic:
202 // What Rustfmt should emit. Mostly corresponds to the `--emit` command line
204 configuration_option_enum! { EmitMode:
207 // Writes the output to stdout.
209 // Displays how much of the input file was processed
213 // Output the changed lines (for internal value only)
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`.
221 // Client-preference for coloured output.
222 configuration_option_enum! { Color:
223 // Always use color, whether it is a piped or terminal output
227 // Automatically use color, if supported by terminal
231 configuration_option_enum! { Version:
239 /// Whether we should use a coloured terminal.
240 pub fn use_colored_tty(self) -> bool {
242 Color::Always => true,
243 Color::Never => false,
244 Color::Auto => atty::is(atty::Stream::Stdout),
249 // How chatty should Rustfmt be?
250 configuration_option_enum! { Verbosity:
254 // Emit as little as possible.
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
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,
282 impl WidthHeuristics {
283 // Using this WidthHeuristics means we ignore heuristics.
284 pub fn null() -> WidthHeuristics {
286 fn_call_width: usize::max_value(),
287 attr_fn_like_width: usize::max_value(),
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,
296 pub fn set(max_width: usize) -> 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,
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
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,
330 impl ::std::str::FromStr for WidthHeuristics {
331 type Err = &'static str;
333 fn from_str(_: &str) -> Result<Self, Self::Err> {
334 Err("WidthHeuristics is not parsable")
338 impl Default for EmitMode {
339 fn default() -> EmitMode {
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.
349 path_set: HashSet<PathBuf>,
350 /// A path to rustfmt.toml.
351 #[serde(skip_serializing)]
352 rustfmt_toml_path: PathBuf,
355 impl<'de> Deserialize<'de> for IgnoreList {
356 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
358 D: Deserializer<'de>,
360 struct HashSetVisitor;
361 impl<'v> Visitor<'v> for HashSetVisitor {
362 type Value = HashSet<PathBuf>;
364 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
365 formatter.write_str("a sequence of path")
368 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
372 let mut path_set = HashSet::new();
373 while let Some(elem) = seq.next_element()? {
374 path_set.insert(elem);
380 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
381 rustfmt_toml_path: PathBuf::new(),
386 impl<'a> IntoIterator for &'a IgnoreList {
387 type Item = &'a PathBuf;
388 type IntoIter = hash_set::Iter<'a, PathBuf>;
390 fn into_iter(self) -> Self::IntoIter {
396 pub fn add_prefix(&mut self, dir: &Path) {
397 self.rustfmt_toml_path = dir.to_path_buf();
400 pub fn rustfmt_toml_path(&self) -> &Path {
401 &self.rustfmt_toml_path
405 impl ::std::str::FromStr for IgnoreList {
406 type Err = &'static str;
408 fn from_str(_: &str) -> Result<Self, Self::Err> {
409 Err("IgnoreList is not parsable")
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>;
420 // The edition of the compiler (RFC 2052)
421 configuration_option_enum! { Edition:
426 impl Default for Edition {
427 fn default() -> Edition {
433 pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
435 Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
436 Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,