1 use std::collections::{hash_set, HashSet};
3 use std::path::{Path, PathBuf};
6 use config_proc_macro::config_type;
7 use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
9 use crate::config::lists::*;
10 use crate::config::Config;
13 pub enum NewlineStyle {
14 /// Auto-detect based on the raw source input.
16 /// Force CRLF (`\r\n`).
20 /// `\r\n` in Windows, `\n`` on other platforms.
25 fn auto_detect(raw_input_text: &str) -> NewlineStyle {
26 if let Some(pos) = raw_input_text.find('\n') {
27 let pos = pos.saturating_sub(1);
28 if let Some('\r') = raw_input_text.chars().nth(pos) {
38 fn native() -> NewlineStyle {
46 /// Apply this newline style to the formatted text. When the style is set
47 /// to `Auto`, the `raw_input_text` is used to detect the existing line
50 /// If the style is set to `Auto` and `raw_input_text` contains no
51 /// newlines, the `Native` style will be used.
52 pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) {
53 use crate::NewlineStyle::*;
56 style = Self::auto_detect(raw_input_text);
59 style = Self::native();
63 let mut transformed = String::with_capacity(2 * formatted_text.capacity());
64 for c in formatted_text.chars() {
66 '\n' => transformed.push_str("\r\n"),
68 c => transformed.push(c),
71 *formatted_text = transformed;
74 Native => unreachable!("NewlineStyle::Native"),
75 Auto => unreachable!("NewlineStyle::Auto"),
81 /// Where to put the opening brace of items (`fn`, `impl`, etc.).
83 /// Put the opening brace on the next line.
85 /// Put the opening brace on the same line, if possible.
87 /// Prefer the same line except where there is a where-clause, in which
88 /// case force the brace to be put on the next line.
93 /// Where to put the opening brace of conditional expressions (`if`, `match`, etc.).
94 pub enum ControlBraceStyle {
95 /// K&R style, Rust community default
105 pub enum IndentStyle {
106 /// First line on the same line as the opening brace, all lines aligned with
109 /// First line is on a new line and all lines align with **block** indent.
114 /// How to place a list-like items.
116 /// Fit as much on one line as possible.
120 /// Place every item on a separate line.
125 /// Spacing around type combinators.
126 pub enum TypeDensity {
127 /// No spaces around "=" and "+"
129 /// Spaces around " = " and " + "
134 /// To what extent does rustfmt pursue its heuristics?
135 pub enum Heuristics {
136 /// Turn off any heuristics
138 /// Turn on max heuristics
140 /// Use Rustfmt's defaults
145 pub fn to_list_tactic(self, len: usize) -> ListTactic {
147 Density::Compressed => ListTactic::Mixed,
148 Density::Tall => ListTactic::HorizontalVertical,
149 Density::Vertical if len == 1 => ListTactic::Horizontal,
150 Density::Vertical => ListTactic::Vertical,
156 pub enum ReportTactic {
162 /// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
168 /// Writes the output to stdout.
170 /// Displays how much of the input file was processed
174 /// Output the changed lines (for internal value only)
176 /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
177 /// quits with exit code 1.
178 /// This option is designed to be run in CI where a non-zero exit signifies
179 /// non-standard code formatting. Used for `--check`.
183 /// Client-preference for coloured output.
186 /// Always use color, whether it is a piped or terminal output
190 /// Automatically use color, if supported by terminal
195 /// rustfmt format style version.
197 /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
199 /// 2.x.y. When specified, rustfmt will formatin the the latest style.
204 /// Whether we should use a coloured terminal.
205 pub fn use_colored_tty(self) -> bool {
207 Color::Always => true,
208 Color::Never => false,
209 Color::Auto => atty::is(atty::Stream::Stdout),
214 /// How chatty should Rustfmt be?
221 /// Emit as little as possible.
225 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
226 pub struct WidthHeuristics {
227 // Maximum width of the args of a function call before falling back
228 // to vertical formatting.
229 pub fn_call_width: usize,
230 // Maximum width of the args of a function-like attributes before falling
231 // back to vertical formatting.
232 pub attr_fn_like_width: usize,
233 // Maximum width in the body of a struct lit before falling back to
234 // vertical formatting.
235 pub struct_lit_width: usize,
236 // Maximum width in the body of a struct variant before falling back
237 // to vertical formatting.
238 pub struct_variant_width: usize,
239 // Maximum width of an array literal before falling back to vertical
241 pub array_width: usize,
242 // Maximum length of a chain to fit on a single line.
243 pub chain_width: usize,
244 // Maximum line length for single line if-else expressions. A value
245 // of zero means always break if-else expressions.
246 pub single_line_if_else_max_width: usize,
249 impl WidthHeuristics {
250 // Using this WidthHeuristics means we ignore heuristics.
251 pub fn null() -> WidthHeuristics {
253 fn_call_width: usize::max_value(),
254 attr_fn_like_width: usize::max_value(),
256 struct_variant_width: 0,
257 array_width: usize::max_value(),
258 chain_width: usize::max_value(),
259 single_line_if_else_max_width: 0,
263 pub fn set(max_width: usize) -> WidthHeuristics {
265 fn_call_width: max_width,
266 attr_fn_like_width: max_width,
267 struct_lit_width: max_width,
268 struct_variant_width: max_width,
269 array_width: max_width,
270 chain_width: max_width,
271 single_line_if_else_max_width: max_width,
275 // scale the default WidthHeuristics according to max_width
276 pub fn scaled(max_width: usize) -> WidthHeuristics {
277 const DEFAULT_MAX_WIDTH: usize = 100;
278 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
279 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
280 // round to the closest 0.1
281 (ratio * 10.0).round() / 10.0
286 fn_call_width: (60.0 * max_width_ratio).round() as usize,
287 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
288 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
289 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
290 array_width: (60.0 * max_width_ratio).round() as usize,
291 chain_width: (60.0 * max_width_ratio).round() as usize,
292 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
297 impl ::std::str::FromStr for WidthHeuristics {
298 type Err = &'static str;
300 fn from_str(_: &str) -> Result<Self, Self::Err> {
301 Err("WidthHeuristics is not parsable")
305 impl Default for EmitMode {
306 fn default() -> EmitMode {
311 /// A set of directories, files and modules that rustfmt should ignore.
312 #[derive(Default, Serialize, Clone, Debug, PartialEq)]
313 pub struct IgnoreList {
314 /// A set of path specified in rustfmt.toml.
316 path_set: HashSet<PathBuf>,
317 /// A path to rustfmt.toml.
318 #[serde(skip_serializing)]
319 rustfmt_toml_path: PathBuf,
322 impl<'de> Deserialize<'de> for IgnoreList {
323 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
325 D: Deserializer<'de>,
327 struct HashSetVisitor;
328 impl<'v> Visitor<'v> for HashSetVisitor {
329 type Value = HashSet<PathBuf>;
331 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
332 formatter.write_str("a sequence of path")
335 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
339 let mut path_set = HashSet::new();
340 while let Some(elem) = seq.next_element()? {
341 path_set.insert(elem);
347 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
348 rustfmt_toml_path: PathBuf::new(),
353 impl<'a> IntoIterator for &'a IgnoreList {
354 type Item = &'a PathBuf;
355 type IntoIter = hash_set::Iter<'a, PathBuf>;
357 fn into_iter(self) -> Self::IntoIter {
363 pub fn add_prefix(&mut self, dir: &Path) {
364 self.rustfmt_toml_path = dir.to_path_buf();
367 pub fn rustfmt_toml_path(&self) -> &Path {
368 &self.rustfmt_toml_path
372 impl ::std::str::FromStr for IgnoreList {
373 type Err = &'static str;
375 fn from_str(_: &str) -> Result<Self, Self::Err> {
376 Err("IgnoreList is not parsable")
380 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
381 /// values in a config with values from the command line.
382 pub trait CliOptions {
383 fn apply_to(self, config: &mut Config);
384 fn config_path(&self) -> Option<&Path>;
387 /// The edition of the syntax and semntics of code (RFC 2052).
400 impl Default for Edition {
401 fn default() -> Edition {
407 pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
409 Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
410 Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
416 fn test_newline_style_auto_detect() {
417 let lf = "One\nTwo\nThree";
418 let crlf = "One\r\nTwo\r\nThree";
419 let none = "One Two Three";
421 assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf));
422 assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf));
423 assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none));
427 fn test_newline_style_auto_apply() {
428 let auto = NewlineStyle::Auto;
430 let formatted_text = "One\nTwo\nThree";
431 let raw_input_text = "One\nTwo\nThree";
433 let mut out = String::from(formatted_text);
434 auto.apply(&mut out, raw_input_text);
435 assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
437 let formatted_text = "One\nTwo\nThree";
438 let raw_input_text = "One\r\nTwo\r\nThree";
440 let mut out = String::from(formatted_text);
441 auto.apply(&mut out, raw_input_text);
442 assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
446 let formatted_text = "One\nTwo\nThree";
447 let raw_input_text = "One Two Three";
449 let mut out = String::from(formatted_text);
450 auto.apply(&mut out, raw_input_text);
452 "One\nTwo\nThree", &out,
453 "auto-native-unix should detect 'lf'"
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\r\nTwo\r\nThree", &out,
466 "auto-native-windows should detect 'crlf'"