1 use std::collections::{hash_set, HashSet};
3 use std::path::{Path, PathBuf};
6 use itertools::Itertools;
7 use rustfmt_config_proc_macro::config_type;
8 use serde::de::{SeqAccess, Visitor};
9 use serde::ser::SerializeSeq;
10 use serde::{Deserialize, Deserializer, Serialize, Serializer};
12 use crate::config::lists::*;
13 use crate::config::Config;
16 pub enum NewlineStyle {
17 /// Auto-detect based on the raw source input.
19 /// Force CRLF (`\r\n`).
23 /// `\r\n` in Windows, `\n` on other platforms.
28 /// Where to put the opening brace of items (`fn`, `impl`, etc.).
30 /// Put the opening brace on the next line.
32 /// Put the opening brace on the same line, if possible.
34 /// Prefer the same line except where there is a where-clause, in which
35 /// case force the brace to be put on the next line.
40 /// Where to put the opening brace of conditional expressions (`if`, `match`, etc.).
41 pub enum ControlBraceStyle {
42 /// K&R style, Rust community default
52 pub enum IndentStyle {
53 /// First line on the same line as the opening brace, all lines aligned with
56 /// First line is on a new line and all lines align with **block** indent.
61 /// How to place a list-like items.
62 /// FIXME: Issue-3581: this should be renamed to ItemsLayout when publishing 2.0
64 /// Fit as much on one line as possible.
66 /// Items are placed horizontally if sufficient space, vertically otherwise.
68 /// Place every item on a separate line.
73 /// Spacing around type combinators.
74 pub enum TypeDensity {
75 /// No spaces around "=" and "+"
77 /// Spaces around " = " and " + "
82 /// Heuristic settings that can be used to simply
83 /// the configuration of the granular width configurations
84 /// like `struct_lit_width`, `array_width`, etc.
86 /// Turn off any heuristics
88 /// Turn on max heuristics
90 /// Use scaled values based on the value of `max_width`
95 pub fn to_list_tactic(self, len: usize) -> ListTactic {
97 Density::Compressed => ListTactic::Mixed,
98 Density::Tall => ListTactic::HorizontalVertical,
99 Density::Vertical if len == 1 => ListTactic::Horizontal,
100 Density::Vertical => ListTactic::Vertical,
106 /// Configuration for import groups, i.e. sets of imports separated by newlines.
107 pub enum GroupImportsTactic {
108 /// Keep groups as they are.
110 /// Discard existing groups, and create new groups for
111 /// 1. `std` / `core` / `alloc` imports
113 /// 3. `self` / `crate` / `super` imports
115 /// Discard existing groups, and create a single group for everything
120 /// How to merge imports.
121 pub enum ImportGranularity {
122 /// Do not merge imports.
124 /// Use one `use` statement per crate.
126 /// Use one `use` statement per module.
128 /// Use one `use` statement per imported item.
130 /// Use one `use` statement including all items.
134 /// Controls how rustfmt should handle case in hexadecimal literals.
136 pub enum HexLiteralCase {
137 /// Leave the literal as-is
139 /// Ensure all literals use uppercase lettering
141 /// Ensure all literals use lowercase lettering
146 pub enum ReportTactic {
152 /// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
158 /// Writes the output to stdout.
160 /// Displays how much of the input file was processed
164 /// Writes the resulting diffs in a JSON format. Returns an empty array
165 /// `[]` if there were no diffs.
167 /// Output the changed lines (for internal value only)
169 /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
170 /// quits with exit code 1.
171 /// This option is designed to be run in CI where a non-zero exit signifies
172 /// non-standard code formatting. Used for `--check`.
176 /// Client-preference for coloured output.
179 /// Always use color, whether it is a piped or terminal output
183 /// Automatically use color, if supported by terminal
188 /// rustfmt format style version.
190 /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
192 /// 2.x.y. When specified, rustfmt will format in the the latest style.
197 /// Whether we should use a coloured terminal.
198 pub fn use_colored_tty(self) -> bool {
200 Color::Always | Color::Auto => true,
201 Color::Never => false,
206 /// How chatty should Rustfmt be?
213 /// Emit as little as possible.
217 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
218 pub struct WidthHeuristics {
219 // Maximum width of the args of a function call before falling back
220 // to vertical formatting.
221 pub(crate) fn_call_width: usize,
222 // Maximum width of the args of a function-like attributes before falling
223 // back to vertical formatting.
224 pub(crate) attr_fn_like_width: usize,
225 // Maximum width in the body of a struct lit before falling back to
226 // vertical formatting.
227 pub(crate) struct_lit_width: usize,
228 // Maximum width in the body of a struct variant before falling back
229 // to vertical formatting.
230 pub(crate) struct_variant_width: usize,
231 // Maximum width of an array literal before falling back to vertical
233 pub(crate) array_width: usize,
234 // Maximum length of a chain to fit on a single line.
235 pub(crate) chain_width: usize,
236 // Maximum line length for single line if-else expressions. A value
237 // of zero means always break if-else expressions.
238 pub(crate) single_line_if_else_max_width: usize,
241 impl fmt::Display for WidthHeuristics {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 write!(f, "{:?}", self)
247 impl WidthHeuristics {
248 // Using this WidthHeuristics means we ignore heuristics.
249 pub fn null() -> WidthHeuristics {
251 fn_call_width: usize::max_value(),
252 attr_fn_like_width: usize::max_value(),
254 struct_variant_width: 0,
255 array_width: usize::max_value(),
256 chain_width: usize::max_value(),
257 single_line_if_else_max_width: 0,
261 pub fn set(max_width: usize) -> WidthHeuristics {
263 fn_call_width: max_width,
264 attr_fn_like_width: max_width,
265 struct_lit_width: max_width,
266 struct_variant_width: max_width,
267 array_width: max_width,
268 chain_width: max_width,
269 single_line_if_else_max_width: max_width,
273 // scale the default WidthHeuristics according to max_width
274 pub fn scaled(max_width: usize) -> WidthHeuristics {
275 const DEFAULT_MAX_WIDTH: usize = 100;
276 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
277 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
278 // round to the closest 0.1
279 (ratio * 10.0).round() / 10.0
284 fn_call_width: (60.0 * max_width_ratio).round() as usize,
285 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
286 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
287 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
288 array_width: (60.0 * max_width_ratio).round() as usize,
289 chain_width: (60.0 * max_width_ratio).round() as usize,
290 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
295 impl ::std::str::FromStr for WidthHeuristics {
296 type Err = &'static str;
298 fn from_str(_: &str) -> Result<Self, Self::Err> {
299 Err("WidthHeuristics is not parsable")
303 impl Default for EmitMode {
304 fn default() -> EmitMode {
309 /// A set of directories, files and modules that rustfmt should ignore.
310 #[derive(Default, Clone, Debug, PartialEq)]
311 pub struct IgnoreList {
312 /// A set of path specified in rustfmt.toml.
313 path_set: HashSet<PathBuf>,
314 /// A path to rustfmt.toml.
315 rustfmt_toml_path: PathBuf,
318 impl fmt::Display for IgnoreList {
319 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325 .format_with(", ", |path, f| f(&format_args!(
327 path.to_string_lossy()
333 impl Serialize for IgnoreList {
334 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
338 let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
339 for e in &self.path_set {
340 seq.serialize_element(e)?;
346 impl<'de> Deserialize<'de> for IgnoreList {
347 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
349 D: Deserializer<'de>,
351 struct HashSetVisitor;
352 impl<'v> Visitor<'v> for HashSetVisitor {
353 type Value = HashSet<PathBuf>;
355 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
356 formatter.write_str("a sequence of path")
359 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
363 let mut path_set = HashSet::new();
364 while let Some(elem) = seq.next_element()? {
365 path_set.insert(elem);
371 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
372 rustfmt_toml_path: PathBuf::new(),
377 impl<'a> IntoIterator for &'a IgnoreList {
378 type Item = &'a PathBuf;
379 type IntoIter = hash_set::Iter<'a, PathBuf>;
381 fn into_iter(self) -> Self::IntoIter {
387 pub fn add_prefix(&mut self, dir: &Path) {
388 self.rustfmt_toml_path = dir.to_path_buf();
391 pub fn rustfmt_toml_path(&self) -> &Path {
392 &self.rustfmt_toml_path
396 impl FromStr for IgnoreList {
397 type Err = &'static str;
399 fn from_str(_: &str) -> Result<Self, Self::Err> {
400 Err("IgnoreList is not parsable")
404 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
405 /// values in a config with values from the command line.
406 pub trait CliOptions {
407 fn apply_to(self, config: &mut Config);
408 fn config_path(&self) -> Option<&Path>;
411 /// The edition of the syntax and semntics of code (RFC 2052).
432 impl Default for Edition {
433 fn default() -> Edition {
438 impl From<Edition> for rustc_span::edition::Edition {
439 fn from(edition: Edition) -> Self {
441 Edition::Edition2015 => Self::Edition2015,
442 Edition::Edition2018 => Self::Edition2018,
443 Edition::Edition2021 => Self::Edition2021,
444 Edition::Edition2024 => Self::Edition2024,
449 impl PartialOrd for Edition {
450 fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
451 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
455 /// Controls how rustfmt should handle leading pipes on match arms.
457 pub enum MatchArmLeadingPipe {
458 /// Place leading pipes on all match arms
460 /// Never emit leading pipes on match arms
462 /// Preserve any existing leading pipes