1 use std::collections::{hash_set, HashSet};
3 use std::path::{Path, PathBuf};
6 use config_proc_macro::config_type;
7 use serde::de::{SeqAccess, Visitor};
8 use serde::{Deserialize, Deserializer, Serialize};
10 use crate::config::lists::*;
11 use crate::config::Config;
14 pub enum NewlineStyle {
15 /// Auto-detect based on the raw source input.
17 /// Force CRLF (`\r\n`).
21 /// `\r\n` in Windows, `\n`` on other platforms.
26 fn auto_detect(raw_input_text: &str) -> NewlineStyle {
27 if let Some(pos) = raw_input_text.find('\n') {
28 let pos = pos.saturating_sub(1);
29 if let Some('\r') = raw_input_text.chars().nth(pos) {
39 fn native() -> NewlineStyle {
47 /// Apply this newline style to the formatted text. When the style is set
48 /// to `Auto`, the `raw_input_text` is used to detect the existing line
51 /// If the style is set to `Auto` and `raw_input_text` contains no
52 /// newlines, the `Native` style will be used.
53 pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) {
54 use crate::NewlineStyle::*;
57 style = Self::auto_detect(raw_input_text);
60 style = Self::native();
64 let mut transformed = String::with_capacity(2 * formatted_text.capacity());
65 for c in formatted_text.chars() {
67 '\n' => transformed.push_str("\r\n"),
69 c => transformed.push(c),
72 *formatted_text = transformed;
75 Native => unreachable!("NewlineStyle::Native"),
76 Auto => unreachable!("NewlineStyle::Auto"),
82 /// Where to put the opening brace of items (`fn`, `impl`, etc.).
84 /// Put the opening brace on the next line.
86 /// Put the opening brace on the same line, if possible.
88 /// Prefer the same line except where there is a where-clause, in which
89 /// case force the brace to be put on the next line.
94 /// Where to put the opening brace of conditional expressions (`if`, `match`, etc.).
95 pub enum ControlBraceStyle {
96 /// K&R style, Rust community default
106 pub enum IndentStyle {
107 /// First line on the same line as the opening brace, all lines aligned with
110 /// First line is on a new line and all lines align with **block** indent.
115 /// How to place a list-like items.
117 /// Fit as much on one line as possible.
121 /// Place every item on a separate line.
126 /// Spacing around type combinators.
127 pub enum TypeDensity {
128 /// No spaces around "=" and "+"
130 /// Spaces around " = " and " + "
135 /// To what extent does rustfmt pursue its heuristics?
136 pub enum Heuristics {
137 /// Turn off any heuristics
139 /// Turn on max heuristics
141 /// Use Rustfmt's defaults
146 pub fn to_list_tactic(self, len: usize) -> ListTactic {
148 Density::Compressed => ListTactic::Mixed,
149 Density::Tall => ListTactic::HorizontalVertical,
150 Density::Vertical if len == 1 => ListTactic::Horizontal,
151 Density::Vertical => ListTactic::Vertical,
157 pub enum ReportTactic {
163 /// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
169 /// Writes the output to stdout.
171 /// Displays how much of the input file was processed
175 /// Output the changed lines (for internal value only)
177 /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
178 /// quits with exit code 1.
179 /// This option is designed to be run in CI where a non-zero exit signifies
180 /// non-standard code formatting. Used for `--check`.
184 /// Client-preference for coloured output.
187 /// Always use color, whether it is a piped or terminal output
191 /// Automatically use color, if supported by terminal
196 /// rustfmt format style version.
198 /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
200 /// 2.x.y. When specified, rustfmt will formatin the the latest style.
205 /// Whether we should use a coloured terminal.
206 pub fn use_colored_tty(self) -> bool {
208 Color::Always => true,
209 Color::Never => false,
210 Color::Auto => atty::is(atty::Stream::Stdout),
215 /// How chatty should Rustfmt be?
222 /// Emit as little as possible.
226 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
227 pub struct WidthHeuristics {
228 // Maximum width of the args of a function call before falling back
229 // to vertical formatting.
230 pub fn_call_width: usize,
231 // Maximum width of the args of a function-like attributes before falling
232 // back to vertical formatting.
233 pub attr_fn_like_width: usize,
234 // Maximum width in the body of a struct lit before falling back to
235 // vertical formatting.
236 pub struct_lit_width: usize,
237 // Maximum width in the body of a struct variant before falling back
238 // to vertical formatting.
239 pub struct_variant_width: usize,
240 // Maximum width of an array literal before falling back to vertical
242 pub array_width: usize,
243 // Maximum length of a chain to fit on a single line.
244 pub chain_width: usize,
245 // Maximum line length for single line if-else expressions. A value
246 // of zero means always break if-else expressions.
247 pub single_line_if_else_max_width: usize,
250 impl WidthHeuristics {
251 // Using this WidthHeuristics means we ignore heuristics.
252 pub fn null() -> WidthHeuristics {
254 fn_call_width: usize::max_value(),
255 attr_fn_like_width: usize::max_value(),
257 struct_variant_width: 0,
258 array_width: usize::max_value(),
259 chain_width: usize::max_value(),
260 single_line_if_else_max_width: 0,
264 pub fn set(max_width: usize) -> WidthHeuristics {
266 fn_call_width: max_width,
267 attr_fn_like_width: max_width,
268 struct_lit_width: max_width,
269 struct_variant_width: max_width,
270 array_width: max_width,
271 chain_width: max_width,
272 single_line_if_else_max_width: max_width,
276 // scale the default WidthHeuristics according to max_width
277 pub fn scaled(max_width: usize) -> WidthHeuristics {
278 const DEFAULT_MAX_WIDTH: usize = 100;
279 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
280 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
281 // round to the closest 0.1
282 (ratio * 10.0).round() / 10.0
287 fn_call_width: (60.0 * max_width_ratio).round() as usize,
288 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
289 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
290 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
291 array_width: (60.0 * max_width_ratio).round() as usize,
292 chain_width: (60.0 * max_width_ratio).round() as usize,
293 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
298 impl ::std::str::FromStr for WidthHeuristics {
299 type Err = &'static str;
301 fn from_str(_: &str) -> Result<Self, Self::Err> {
302 Err("WidthHeuristics is not parsable")
306 impl Default for EmitMode {
307 fn default() -> EmitMode {
312 /// A set of directories, files and modules that rustfmt should ignore.
313 #[derive(Default, Serialize, Clone, Debug, PartialEq)]
314 pub struct IgnoreList {
315 /// A set of path specified in rustfmt.toml.
317 path_set: HashSet<PathBuf>,
318 /// A path to rustfmt.toml.
319 #[serde(skip_serializing)]
320 rustfmt_toml_path: PathBuf,
323 impl<'de> Deserialize<'de> for IgnoreList {
324 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
326 D: Deserializer<'de>,
328 struct HashSetVisitor;
329 impl<'v> Visitor<'v> for HashSetVisitor {
330 type Value = HashSet<PathBuf>;
332 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
333 formatter.write_str("a sequence of path")
336 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
340 let mut path_set = HashSet::new();
341 while let Some(elem) = seq.next_element()? {
342 path_set.insert(elem);
348 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
349 rustfmt_toml_path: PathBuf::new(),
354 impl<'a> IntoIterator for &'a IgnoreList {
355 type Item = &'a PathBuf;
356 type IntoIter = hash_set::Iter<'a, PathBuf>;
358 fn into_iter(self) -> Self::IntoIter {
364 pub fn add_prefix(&mut self, dir: &Path) {
365 self.rustfmt_toml_path = dir.to_path_buf();
368 pub fn rustfmt_toml_path(&self) -> &Path {
369 &self.rustfmt_toml_path
373 impl ::std::str::FromStr for IgnoreList {
374 type Err = &'static str;
376 fn from_str(_: &str) -> Result<Self, Self::Err> {
377 Err("IgnoreList is not parsable")
381 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
382 /// values in a config with values from the command line.
383 pub trait CliOptions {
384 fn apply_to(self, config: &mut Config);
385 fn config_path(&self) -> Option<&Path>;
388 /// The edition of the syntax and semntics of code (RFC 2052).
401 impl Default for Edition {
402 fn default() -> Edition {
408 pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
410 Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
411 Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
417 fn test_newline_style_auto_detect() {
418 let lf = "One\nTwo\nThree";
419 let crlf = "One\r\nTwo\r\nThree";
420 let none = "One Two Three";
422 assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf));
423 assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf));
424 assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none));
428 fn test_newline_style_auto_apply() {
429 let auto = NewlineStyle::Auto;
431 let formatted_text = "One\nTwo\nThree";
432 let raw_input_text = "One\nTwo\nThree";
434 let mut out = String::from(formatted_text);
435 auto.apply(&mut out, raw_input_text);
436 assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
438 let formatted_text = "One\nTwo\nThree";
439 let raw_input_text = "One\r\nTwo\r\nThree";
441 let mut out = String::from(formatted_text);
442 auto.apply(&mut out, raw_input_text);
443 assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
447 let formatted_text = "One\nTwo\nThree";
448 let raw_input_text = "One Two Three";
450 let mut out = String::from(formatted_text);
451 auto.apply(&mut out, raw_input_text);
453 "One\nTwo\nThree", &out,
454 "auto-native-unix should detect 'lf'"
460 let formatted_text = "One\nTwo\nThree";
461 let raw_input_text = "One Two Three";
463 let mut out = String::from(formatted_text);
464 auto.apply(&mut out, raw_input_text);
466 "One\r\nTwo\r\nThree", &out,
467 "auto-native-windows should detect 'crlf'"