| #![allow(unused_imports)] |
| |
| use std::collections::{hash_set, HashSet}; |
| use std::fmt; |
| use std::path::{Path, PathBuf}; |
| use std::str::FromStr; |
| |
| use itertools::Itertools; |
| use rustfmt_config_proc_macro::config_type; |
| use serde::de::{SeqAccess, Visitor}; |
| use serde::ser::SerializeSeq; |
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; |
| |
| use crate::config::lists::*; |
| use crate::config::Config; |
| |
| #[config_type] |
| pub enum NewlineStyle { |
| /// Auto-detect based on the raw source input. |
| Auto, |
| /// Force CRLF (`\r\n`). |
| Windows, |
| /// Force CR (`\n`). |
| Unix, |
| /// `\r\n` in Windows, `\n` on other platforms. |
| Native, |
| } |
| |
| #[config_type] |
| /// Where to put the opening brace of items (`fn`, `impl`, etc.). |
| pub enum BraceStyle { |
| /// Put the opening brace on the next line. |
| AlwaysNextLine, |
| /// Put the opening brace on the same line, if possible. |
| PreferSameLine, |
| /// Prefer the same line except where there is a where-clause, in which |
| /// case force the brace to be put on the next line. |
| SameLineWhere, |
| } |
| |
| #[config_type] |
| /// Where to put the opening brace of conditional expressions (`if`, `match`, etc.). |
| pub enum ControlBraceStyle { |
| /// K&R style, Rust community default |
| AlwaysSameLine, |
| /// Stroustrup style |
| ClosingNextLine, |
| /// Allman style |
| AlwaysNextLine, |
| } |
| |
| #[config_type] |
| /// How to indent. |
| pub enum IndentStyle { |
| /// First line on the same line as the opening brace, all lines aligned with |
| /// the first line. |
| Visual, |
| /// First line is on a new line and all lines align with **block** indent. |
| Block, |
| } |
| |
| #[config_type] |
| /// How to place a list-like items. |
| /// FIXME: Issue-3581: this should be renamed to ItemsLayout when publishing 2.0 |
| pub enum Density { |
| /// Fit as much on one line as possible. |
| Compressed, |
| /// Items are placed horizontally if sufficient space, vertically otherwise. |
| Tall, |
| /// Place every item on a separate line. |
| Vertical, |
| } |
| |
| #[config_type] |
| /// Spacing around type combinators. |
| pub enum TypeDensity { |
| /// No spaces around "=" and "+" |
| Compressed, |
| /// Spaces around " = " and " + " |
| Wide, |
| } |
| |
| #[config_type] |
| /// Heuristic settings that can be used to simply |
| /// the configuration of the granular width configurations |
| /// like `struct_lit_width`, `array_width`, etc. |
| pub enum Heuristics { |
| /// Turn off any heuristics |
| Off, |
| /// Turn on max heuristics |
| Max, |
| /// Use scaled values based on the value of `max_width` |
| Default, |
| } |
| |
| impl Density { |
| pub fn to_list_tactic(self, len: usize) -> ListTactic { |
| match self { |
| Density::Compressed => ListTactic::Mixed, |
| Density::Tall => ListTactic::HorizontalVertical, |
| Density::Vertical if len == 1 => ListTactic::Horizontal, |
| Density::Vertical => ListTactic::Vertical, |
| } |
| } |
| } |
| |
| #[config_type] |
| /// Configuration for import groups, i.e. sets of imports separated by newlines. |
| pub enum GroupImportsTactic { |
| /// Keep groups as they are. |
| Preserve, |
| /// Discard existing groups, and create new groups for |
| /// 1. `std` / `core` / `alloc` imports |
| /// 2. other imports |
| /// 3. `self` / `crate` / `super` imports |
| StdExternalCrate, |
| /// Discard existing groups, and create a single group for everything |
| One, |
| } |
| |
| #[config_type] |
| /// How to merge imports. |
| pub enum ImportGranularity { |
| /// Do not merge imports. |
| Preserve, |
| /// Use one `use` statement per crate. |
| Crate, |
| /// Use one `use` statement per module. |
| Module, |
| /// Use one `use` statement per imported item. |
| Item, |
| /// Use one `use` statement including all items. |
| One, |
| } |
| |
| /// Controls how rustfmt should handle case in hexadecimal literals. |
| #[config_type] |
| pub enum HexLiteralCase { |
| /// Leave the literal as-is |
| Preserve, |
| /// Ensure all literals use uppercase lettering |
| Upper, |
| /// Ensure all literals use lowercase lettering |
| Lower, |
| } |
| |
| #[config_type] |
| pub enum ReportTactic { |
| Always, |
| Unnumbered, |
| Never, |
| } |
| |
| /// What Rustfmt should emit. Mostly corresponds to the `--emit` command line |
| /// option. |
| #[config_type] |
| pub enum EmitMode { |
| /// Emits to files. |
| Files, |
| /// Writes the output to stdout. |
| Stdout, |
| /// Displays how much of the input file was processed |
| Coverage, |
| /// Unfancy stdout |
| Checkstyle, |
| /// Writes the resulting diffs in a JSON format. Returns an empty array |
| /// `[]` if there were no diffs. |
| Json, |
| /// Output the changed lines (for internal value only) |
| ModifiedLines, |
| /// Checks if a diff can be generated. If so, rustfmt outputs a diff and |
| /// quits with exit code 1. |
| /// This option is designed to be run in CI where a non-zero exit signifies |
| /// non-standard code formatting. Used for `--check`. |
| Diff, |
| } |
| |
| /// Client-preference for coloured output. |
| #[config_type] |
| pub enum Color { |
| /// Always use color, whether it is a piped or terminal output |
| Always, |
| /// Never use color |
| Never, |
| /// Automatically use color, if supported by terminal |
| Auto, |
| } |
| |
| #[config_type] |
| /// rustfmt format style version. |
| pub enum Version { |
| /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0. |
| One, |
| /// 2.x.y. When specified, rustfmt will format in the the latest style. |
| Two, |
| } |
| |
| impl Color { |
| /// Whether we should use a coloured terminal. |
| pub fn use_colored_tty(self) -> bool { |
| match self { |
| Color::Always | Color::Auto => true, |
| Color::Never => false, |
| } |
| } |
| } |
| |
| /// How chatty should Rustfmt be? |
| #[config_type] |
| pub enum Verbosity { |
| /// Emit more. |
| Verbose, |
| /// Default. |
| Normal, |
| /// Emit as little as possible. |
| Quiet, |
| } |
| |
| #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] |
| pub struct WidthHeuristics { |
| // Maximum width of the args of a function call before falling back |
| // to vertical formatting. |
| pub(crate) fn_call_width: usize, |
| // Maximum width of the args of a function-like attributes before falling |
| // back to vertical formatting. |
| pub(crate) attr_fn_like_width: usize, |
| // Maximum width in the body of a struct lit before falling back to |
| // vertical formatting. |
| pub(crate) struct_lit_width: usize, |
| // Maximum width in the body of a struct variant before falling back |
| // to vertical formatting. |
| pub(crate) struct_variant_width: usize, |
| // Maximum width of an array literal before falling back to vertical |
| // formatting. |
| pub(crate) array_width: usize, |
| // Maximum length of a chain to fit on a single line. |
| pub(crate) chain_width: usize, |
| // Maximum line length for single line if-else expressions. A value |
| // of zero means always break if-else expressions. |
| pub(crate) single_line_if_else_max_width: usize, |
| // Maximum line length for single line let-else statements. A value of zero means |
| // always format the divergent `else` block over multiple lines. |
| pub(crate) single_line_let_else_max_width: usize, |
| } |
| |
| impl fmt::Display for WidthHeuristics { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{self:?}") |
| } |
| } |
| |
| impl WidthHeuristics { |
| // Using this WidthHeuristics means we ignore heuristics. |
| pub fn null() -> WidthHeuristics { |
| WidthHeuristics { |
| fn_call_width: usize::max_value(), |
| attr_fn_like_width: usize::max_value(), |
| struct_lit_width: 0, |
| struct_variant_width: 0, |
| array_width: usize::max_value(), |
| chain_width: usize::max_value(), |
| single_line_if_else_max_width: 0, |
| single_line_let_else_max_width: 0, |
| } |
| } |
| |
| pub fn set(max_width: usize) -> WidthHeuristics { |
| WidthHeuristics { |
| fn_call_width: max_width, |
| attr_fn_like_width: max_width, |
| struct_lit_width: max_width, |
| struct_variant_width: max_width, |
| array_width: max_width, |
| chain_width: max_width, |
| single_line_if_else_max_width: max_width, |
| single_line_let_else_max_width: max_width, |
| } |
| } |
| |
| // scale the default WidthHeuristics according to max_width |
| pub fn scaled(max_width: usize) -> WidthHeuristics { |
| const DEFAULT_MAX_WIDTH: usize = 100; |
| let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH { |
| let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32; |
| // round to the closest 0.1 |
| (ratio * 10.0).round() / 10.0 |
| } else { |
| 1.0 |
| }; |
| WidthHeuristics { |
| fn_call_width: (60.0 * max_width_ratio).round() as usize, |
| attr_fn_like_width: (70.0 * max_width_ratio).round() as usize, |
| struct_lit_width: (18.0 * max_width_ratio).round() as usize, |
| struct_variant_width: (35.0 * max_width_ratio).round() as usize, |
| array_width: (60.0 * max_width_ratio).round() as usize, |
| chain_width: (60.0 * max_width_ratio).round() as usize, |
| single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize, |
| single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize, |
| } |
| } |
| } |
| |
| impl ::std::str::FromStr for WidthHeuristics { |
| type Err = &'static str; |
| |
| fn from_str(_: &str) -> Result<Self, Self::Err> { |
| Err("WidthHeuristics is not parsable") |
| } |
| } |
| |
| impl Default for EmitMode { |
| fn default() -> EmitMode { |
| EmitMode::Files |
| } |
| } |
| |
| /// A set of directories, files and modules that rustfmt should ignore. |
| #[derive(Default, Clone, Debug, PartialEq)] |
| pub struct IgnoreList { |
| /// A set of path specified in rustfmt.toml. |
| path_set: HashSet<PathBuf>, |
| /// A path to rustfmt.toml. |
| rustfmt_toml_path: PathBuf, |
| } |
| |
| impl fmt::Display for IgnoreList { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "[{}]", |
| self.path_set |
| .iter() |
| .format_with(", ", |path, f| f(&format_args!( |
| "{}", |
| path.to_string_lossy() |
| ))) |
| ) |
| } |
| } |
| |
| impl Serialize for IgnoreList { |
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| where |
| S: Serializer, |
| { |
| let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?; |
| for e in &self.path_set { |
| seq.serialize_element(e)?; |
| } |
| seq.end() |
| } |
| } |
| |
| impl<'de> Deserialize<'de> for IgnoreList { |
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| where |
| D: Deserializer<'de>, |
| { |
| struct HashSetVisitor; |
| impl<'v> Visitor<'v> for HashSetVisitor { |
| type Value = HashSet<PathBuf>; |
| |
| fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { |
| formatter.write_str("a sequence of path") |
| } |
| |
| fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> |
| where |
| A: SeqAccess<'v>, |
| { |
| let mut path_set = HashSet::new(); |
| while let Some(elem) = seq.next_element()? { |
| path_set.insert(elem); |
| } |
| Ok(path_set) |
| } |
| } |
| Ok(IgnoreList { |
| path_set: deserializer.deserialize_seq(HashSetVisitor)?, |
| rustfmt_toml_path: PathBuf::new(), |
| }) |
| } |
| } |
| |
| impl<'a> IntoIterator for &'a IgnoreList { |
| type Item = &'a PathBuf; |
| type IntoIter = hash_set::Iter<'a, PathBuf>; |
| |
| fn into_iter(self) -> Self::IntoIter { |
| self.path_set.iter() |
| } |
| } |
| |
| impl IgnoreList { |
| pub fn add_prefix(&mut self, dir: &Path) { |
| self.rustfmt_toml_path = dir.to_path_buf(); |
| } |
| |
| pub fn rustfmt_toml_path(&self) -> &Path { |
| &self.rustfmt_toml_path |
| } |
| } |
| |
| impl FromStr for IgnoreList { |
| type Err = &'static str; |
| |
| fn from_str(_: &str) -> Result<Self, Self::Err> { |
| Err("IgnoreList is not parsable") |
| } |
| } |
| |
| /// Maps client-supplied options to Rustfmt's internals, mostly overriding |
| /// values in a config with values from the command line. |
| pub trait CliOptions { |
| fn apply_to(self, config: &mut Config); |
| fn config_path(&self) -> Option<&Path>; |
| } |
| |
| /// The edition of the syntax and semntics of code (RFC 2052). |
| #[config_type] |
| pub enum Edition { |
| #[value = "2015"] |
| #[doc_hint = "2015"] |
| /// Edition 2015. |
| Edition2015, |
| #[value = "2018"] |
| #[doc_hint = "2018"] |
| /// Edition 2018. |
| Edition2018, |
| #[value = "2021"] |
| #[doc_hint = "2021"] |
| /// Edition 2021. |
| Edition2021, |
| #[value = "2024"] |
| #[doc_hint = "2024"] |
| /// Edition 2024. |
| Edition2024, |
| } |
| |
| impl Default for Edition { |
| fn default() -> Edition { |
| Edition::Edition2015 |
| } |
| } |
| |
| impl From<Edition> for rustc_span::edition::Edition { |
| fn from(edition: Edition) -> Self { |
| match edition { |
| Edition::Edition2015 => Self::Edition2015, |
| Edition::Edition2018 => Self::Edition2018, |
| Edition::Edition2021 => Self::Edition2021, |
| Edition::Edition2024 => Self::Edition2024, |
| } |
| } |
| } |
| |
| impl PartialOrd for Edition { |
| fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> { |
| rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) |
| } |
| } |
| |
| /// Controls how rustfmt should handle leading pipes on match arms. |
| #[config_type] |
| pub enum MatchArmLeadingPipe { |
| /// Place leading pipes on all match arms |
| Always, |
| /// Never emit leading pipes on match arms |
| Never, |
| /// Preserve any existing leading pipes |
| Preserve, |
| } |