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 syntax::codemap::FileName;
13 use config::config_type::ConfigType;
16 use std::collections::HashSet;
17 use std::path::{Path, PathBuf};
19 /// Macro for deriving implementations of Serialize/Deserialize for enums
21 macro_rules! impl_enum_serialize_and_deserialize {
22 ( $e:ident, $( $x:ident ),* ) => {
23 impl ::serde::ser::Serialize for $e {
24 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25 where S: ::serde::ser::Serializer
27 use serde::ser::Error;
29 // We don't know whether the user of the macro has given us all options.
30 #[allow(unreachable_patterns)]
33 $e::$x => serializer.serialize_str(stringify!($x)),
36 Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
42 impl<'de> ::serde::de::Deserialize<'de> for $e {
43 fn deserialize<D>(d: D) -> Result<Self, D::Error>
44 where D: ::serde::Deserializer<'de> {
45 use serde::de::{Error, Visitor};
46 use std::marker::PhantomData;
48 struct StringOnly<T>(PhantomData<T>);
49 impl<'de, T> Visitor<'de> for StringOnly<T>
50 where T: ::serde::Deserializer<'de> {
52 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
53 formatter.write_str("string")
55 fn visit_str<E>(self, value: &str) -> Result<String, E> {
56 Ok(String::from(value))
59 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
61 if stringify!($x).eq_ignore_ascii_case(&s) {
65 static ALLOWED: &'static[&str] = &[$(stringify!($x),)*];
66 Err(D::Error::unknown_variant(&s, ALLOWED))
70 impl ::std::str::FromStr for $e {
71 type Err = &'static str;
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
75 if stringify!($x).eq_ignore_ascii_case(s) {
83 impl ConfigType for $e {
84 fn doc_hint() -> String {
85 let mut variants = Vec::new();
87 variants.push(stringify!($x));
89 format!("[{}]", variants.join("|"))
95 macro_rules! configuration_option_enum{
96 ($e:ident: $( $x:ident ),+ $(,)*) => {
97 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
102 impl_enum_serialize_and_deserialize!($e, $( $x ),+);
106 configuration_option_enum! { NewlineStyle:
109 Native, // \r\n in Windows, \n on other platforms
112 configuration_option_enum! { BraceStyle:
115 // Prefer same line except where there is a where clause, in which case force
116 // the brace to the next line.
120 configuration_option_enum! { ControlBraceStyle:
121 // K&R style, Rust community default
129 configuration_option_enum! { IndentStyle:
130 // First line on the same line as the opening brace, all lines aligned with
133 // First line is on a new line and all lines align with block indent.
137 configuration_option_enum! { Density:
138 // Fit as much on one line as possible.
142 // Place every item on a separate line.
146 configuration_option_enum! { TypeDensity:
147 // No spaces around "=" and "+"
149 // Spaces around " = " and " + "
154 pub fn to_list_tactic(self) -> ListTactic {
156 Density::Compressed => ListTactic::Mixed,
157 Density::Tall => ListTactic::HorizontalVertical,
158 Density::Vertical => ListTactic::Vertical,
163 configuration_option_enum! { ReportTactic:
169 configuration_option_enum! { WriteMode:
170 // Backs the original file up and overwrites the original.
172 // Overwrites original file without backup.
174 // Writes the output to stdout.
176 // Writes the diff to stdout.
178 // Displays how much of the input file was processed
182 // Outputs a checkstyle XML file.
184 // Output the changed lines (for internal value only)
188 configuration_option_enum! { Color:
189 // Always use color, whether it is a piped or terminal output
193 // Automatically use color, if supported by terminal
197 #[derive(Deserialize, Serialize, Clone, Debug)]
198 pub struct WidthHeuristics {
199 // Maximum width of the args of a function call before falling back
200 // to vertical formatting.
201 pub fn_call_width: usize,
202 // Maximum width in the body of a struct lit before falling back to
203 // vertical formatting.
204 pub struct_lit_width: usize,
205 // Maximum width in the body of a struct variant before falling back
206 // to vertical formatting.
207 pub struct_variant_width: usize,
208 // Maximum width of an array literal before falling back to vertical
210 pub array_width: usize,
211 // Maximum length of a chain to fit on a single line.
212 pub chain_width: usize,
213 // Maximum line length for single line if-else expressions. A value
214 // of zero means always break if-else expressions.
215 pub single_line_if_else_max_width: usize,
218 impl WidthHeuristics {
219 // Using this WidthHeuristics means we ignore heuristics.
220 pub fn null() -> WidthHeuristics {
222 fn_call_width: usize::max_value(),
224 struct_variant_width: 0,
225 array_width: usize::max_value(),
226 chain_width: usize::max_value(),
227 single_line_if_else_max_width: 0,
230 // scale the default WidthHeuristics according to max_width
231 pub fn scaled(max_width: usize) -> WidthHeuristics {
232 let mut max_width_ratio: f32 = max_width as f32 / 100.0; // 100 is the default width -> default ratio is 1
233 max_width_ratio = (max_width_ratio * 10.0).round() / 10.0; // round to the closest 0.1
235 fn_call_width: (60.0 * max_width_ratio).round() as usize,
236 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
237 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
238 array_width: (60.0 * max_width_ratio).round() as usize,
239 chain_width: (60.0 * max_width_ratio).round() as usize,
240 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
245 impl ::std::str::FromStr for WidthHeuristics {
246 type Err = &'static str;
248 fn from_str(_: &str) -> Result<Self, Self::Err> {
249 Err("WidthHeuristics is not parsable")
253 /// A set of directories, files and modules that rustfmt should ignore.
254 #[derive(Default, Deserialize, Serialize, Clone, Debug)]
255 pub struct IgnoreList {
256 directories: Option<HashSet<PathBuf>>,
257 files: Option<HashSet<PathBuf>>,
261 fn add_prefix_inner(set: &HashSet<PathBuf>, dir: &Path) -> HashSet<PathBuf> {
267 let mut path = PathBuf::from(dir);
275 pub fn add_prefix(&mut self, dir: &Path) {
276 macro add_prefix_inner_with ($($field: ident),* $(,)*) {
277 $(if let Some(set) = self.$field.as_mut() {
278 *set = IgnoreList::add_prefix_inner(set, dir);
282 add_prefix_inner_with!(directories, files);
285 fn is_ignore_file(&self, path: &Path) -> bool {
286 self.files.as_ref().map_or(false, |set| set.contains(path))
289 fn is_under_ignore_dir(&self, path: &Path) -> bool {
290 if let Some(ref dirs) = self.directories {
292 if path.starts_with(dir) {
301 pub fn skip_file(&self, file: &FileName) -> bool {
302 if let FileName::Real(ref path) = file {
303 self.is_ignore_file(path) || self.is_under_ignore_dir(path)
310 impl ::std::str::FromStr for IgnoreList {
311 type Err = &'static str;
313 fn from_str(_: &str) -> Result<Self, Self::Err> {
314 Err("IgnoreList is not parsable")