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 std::collections::HashSet;
16 use std::path::{Path, PathBuf};
18 /// Macro for deriving implementations of Serialize/Deserialize for enums
20 macro_rules! impl_enum_serialize_and_deserialize {
21 ( $e:ident, $( $x:ident ),* ) => {
22 impl ::serde::ser::Serialize for $e {
23 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
24 where S: ::serde::ser::Serializer
26 use serde::ser::Error;
28 // We don't know whether the user of the macro has given us all options.
29 #[allow(unreachable_patterns)]
32 $e::$x => serializer.serialize_str(stringify!($x)),
35 Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
41 impl<'de> ::serde::de::Deserialize<'de> for $e {
42 fn deserialize<D>(d: D) -> Result<Self, D::Error>
43 where D: ::serde::Deserializer<'de> {
44 use serde::de::{Error, Visitor};
45 use std::marker::PhantomData;
47 struct StringOnly<T>(PhantomData<T>);
48 impl<'de, T> Visitor<'de> for StringOnly<T>
49 where T: ::serde::Deserializer<'de> {
51 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
52 formatter.write_str("string")
54 fn visit_str<E>(self, value: &str) -> Result<String, E> {
55 Ok(String::from(value))
58 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
60 if stringify!($x).eq_ignore_ascii_case(&s) {
64 static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
65 Err(D::Error::unknown_variant(&s, ALLOWED))
69 impl ::std::str::FromStr for $e {
70 type Err = &'static str;
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 if stringify!($x).eq_ignore_ascii_case(s) {
82 impl ConfigType for $e {
83 fn doc_hint() -> String {
84 let mut variants = Vec::new();
86 variants.push(stringify!($x));
88 format!("[{}]", variants.join("|"))
94 macro_rules! configuration_option_enum{
95 ($e:ident: $( $x:ident ),+ $(,)*) => {
96 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
101 impl_enum_serialize_and_deserialize!($e, $( $x ),+);
105 configuration_option_enum! { NewlineStyle:
108 Native, // \r\n in Windows, \n on other platforms
111 configuration_option_enum! { BraceStyle:
114 // Prefer same line except where there is a where clause, in which case force
115 // the brace to the next line.
119 configuration_option_enum! { ControlBraceStyle:
120 // K&R style, Rust community default
128 configuration_option_enum! { IndentStyle:
129 // First line on the same line as the opening brace, all lines aligned with
132 // First line is on a new line and all lines align with block indent.
136 configuration_option_enum! { Density:
137 // Fit as much on one line as possible.
141 // Place every item on a separate line.
145 configuration_option_enum! { TypeDensity:
146 // No spaces around "=" and "+"
148 // Spaces around " = " and " + "
153 pub fn to_list_tactic(self) -> ListTactic {
155 Density::Compressed => ListTactic::Mixed,
156 Density::Tall => ListTactic::HorizontalVertical,
157 Density::Vertical => ListTactic::Vertical,
162 configuration_option_enum! { ReportTactic:
168 configuration_option_enum! { WriteMode:
169 // Overwrites original file without backup.
171 // Backs the original file up and overwrites the original.
173 // Writes the output to stdout.
175 // Displays how much of the input file was processed
179 // Output the changed lines (for internal value only)
181 // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
182 // This option is designed to be run in CI where a non-zero exit signifies non-standard code
185 // Rustfmt shouldn't output anything formatting-like (e.g., emit a help message).
189 configuration_option_enum! { Color:
190 // Always use color, whether it is a piped or terminal output
194 // Automatically use color, if supported by terminal
198 configuration_option_enum! { Verbosity:
202 // Emit as little as possible.
206 #[derive(Deserialize, Serialize, Clone, Debug)]
207 pub struct WidthHeuristics {
208 // Maximum width of the args of a function call before falling back
209 // to vertical formatting.
210 pub fn_call_width: usize,
211 // Maximum width in the body of a struct lit before falling back to
212 // vertical formatting.
213 pub struct_lit_width: usize,
214 // Maximum width in the body of a struct variant before falling back
215 // to vertical formatting.
216 pub struct_variant_width: usize,
217 // Maximum width of an array literal before falling back to vertical
219 pub array_width: usize,
220 // Maximum length of a chain to fit on a single line.
221 pub chain_width: usize,
222 // Maximum line length for single line if-else expressions. A value
223 // of zero means always break if-else expressions.
224 pub single_line_if_else_max_width: usize,
227 impl WidthHeuristics {
228 // Using this WidthHeuristics means we ignore heuristics.
229 pub fn null() -> WidthHeuristics {
231 fn_call_width: usize::max_value(),
233 struct_variant_width: 0,
234 array_width: usize::max_value(),
235 chain_width: usize::max_value(),
236 single_line_if_else_max_width: 0,
240 // scale the default WidthHeuristics according to max_width
241 pub fn scaled(max_width: usize) -> WidthHeuristics {
242 const DEFAULT_MAX_WIDTH: usize = 100;
243 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
244 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
245 // round to the closest 0.1
246 (ratio * 10.0).round() / 10.0
251 fn_call_width: (60.0 * max_width_ratio).round() as usize,
252 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
253 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
254 array_width: (60.0 * max_width_ratio).round() as usize,
255 chain_width: (60.0 * max_width_ratio).round() as usize,
256 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
261 impl ::std::str::FromStr for WidthHeuristics {
262 type Err = &'static str;
264 fn from_str(_: &str) -> Result<Self, Self::Err> {
265 Err("WidthHeuristics is not parsable")
269 impl Default for WriteMode {
270 fn default() -> WriteMode {
275 /// A set of directories, files and modules that rustfmt should ignore.
276 #[derive(Default, Deserialize, Serialize, Clone, Debug)]
277 pub struct IgnoreList(HashSet<PathBuf>);
280 pub fn add_prefix(&mut self, dir: &Path) {
288 let mut path = PathBuf::from(dir);
296 fn skip_file_inner(&self, file: &Path) -> bool {
297 self.0.iter().any(|path| file.starts_with(path))
300 pub fn skip_file(&self, file: &FileName) -> bool {
301 if let FileName::Real(ref path) = file {
302 self.skip_file_inner(path)
309 impl ::std::str::FromStr for IgnoreList {
310 type Err = &'static str;
312 fn from_str(_: &str) -> Result<Self, Self::Err> {
313 Err("IgnoreList is not parsable")
317 pub trait CliOptions {
318 fn apply_to(self, config: &mut Config);
319 fn config_path(&self) -> Option<&Path>;