1#![allow(unused_imports)]
2
3use std::collections::{HashSet, hash_set};
4use std::fmt;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7
8use itertools::Itertools;
9use rustfmt_config_proc_macro::config_type;
10use serde::de::{SeqAccess, Visitor};
11use serde::ser::SerializeSeq;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13
14use crate::config::Config;
15use crate::config::file_lines::FileLines;
16use crate::config::lists::*;
17use crate::config::macro_names::MacroSelectors;
18
19#[config_type]
20pub enum NewlineStyle {
21 Auto,
23 Windows,
25 Unix,
27 Native,
29}
30
31#[config_type]
32pub enum BraceStyle {
34 AlwaysNextLine,
36 PreferSameLine,
38 SameLineWhere,
41}
42
43#[config_type]
44pub enum ControlBraceStyle {
46 AlwaysSameLine,
48 ClosingNextLine,
50 AlwaysNextLine,
52}
53
54#[config_type]
55pub enum IndentStyle {
57 Visual,
60 Block,
62}
63
64#[config_type]
65pub enum Density {
68 Compressed,
70 Tall,
72 Vertical,
74}
75
76#[config_type]
77pub enum TypeDensity {
79 Compressed,
81 Wide,
83}
84
85#[config_type]
86pub enum Heuristics {
90 Off,
92 Max,
94 Default,
96}
97
98#[config_type]
99pub enum DocCodeHeuristics {
102 Inherit,
104 Off,
106 Max,
108 Default,
110}
111
112impl DocCodeHeuristics {
113 pub fn to_heuristics(self) -> Option<Heuristics> {
114 match self {
115 DocCodeHeuristics::Inherit => None,
116 DocCodeHeuristics::Off => Some(Heuristics::Off),
117 DocCodeHeuristics::Max => Some(Heuristics::Max),
118 DocCodeHeuristics::Default => Some(Heuristics::Default),
119 }
120 }
121}
122
123impl Density {
124 pub fn to_list_tactic(self, style_edition: StyleEdition, len: usize) -> ListTactic {
125 match self {
126 Density::Compressed => ListTactic::Mixed,
127 Density::Tall => ListTactic::HorizontalVertical,
128 Density::Vertical if len == 1 => {
129 if style_edition <= StyleEdition::Edition2024 {
130 ListTactic::Horizontal
131 } else {
132 ListTactic::HorizontalVertical
133 }
134 }
135 Density::Vertical => ListTactic::Vertical,
136 }
137 }
138}
139
140#[config_type]
141pub enum GroupImportsTactic {
143 Preserve,
145 StdExternalCrate,
150 One,
152}
153
154#[config_type]
155pub enum ImportGranularity {
157 Preserve,
159 Crate,
161 Module,
163 Item,
165 One,
167}
168
169#[config_type]
171pub enum HexLiteralCase {
172 Preserve,
174 Upper,
176 Lower,
178}
179
180#[config_type]
182pub enum FloatLiteralTrailingZero {
183 Preserve,
185 Always,
187 IfNoPostfix,
190 Never,
193}
194
195#[config_type]
196pub enum ReportTactic {
197 Always,
198 Unnumbered,
199 Never,
200}
201
202#[config_type]
205pub enum EmitMode {
206 Files,
208 Stdout,
210 Coverage,
212 Checkstyle,
214 Json,
217 ModifiedLines,
219 Diff,
224}
225
226#[config_type]
228pub enum Color {
229 Always,
231 Never,
233 Auto,
235}
236
237#[config_type]
238pub enum Version {
240 One,
242 Two,
244}
245
246impl Color {
247 pub fn use_colored_tty(self) -> bool {
249 match self {
250 Color::Always | Color::Auto => true,
251 Color::Never => false,
252 }
253 }
254}
255
256#[config_type]
258pub enum Verbosity {
259 Verbose,
261 Normal,
263 Quiet,
265}
266
267#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
268pub struct WidthHeuristics {
269 pub(crate) fn_call_width: usize,
272 pub(crate) attr_fn_like_width: usize,
275 pub(crate) struct_lit_width: usize,
278 pub(crate) struct_variant_width: usize,
281 pub(crate) array_width: usize,
284 pub(crate) chain_width: usize,
286 pub(crate) single_line_if_else_max_width: usize,
289 pub(crate) single_line_let_else_max_width: usize,
292}
293
294impl fmt::Display for WidthHeuristics {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 write!(f, "{self:?}")
297 }
298}
299
300impl WidthHeuristics {
301 pub fn null() -> WidthHeuristics {
303 WidthHeuristics {
304 fn_call_width: usize::MAX,
305 attr_fn_like_width: usize::MAX,
306 struct_lit_width: 0,
307 struct_variant_width: 0,
308 array_width: usize::MAX,
309 chain_width: usize::MAX,
310 single_line_if_else_max_width: 0,
311 single_line_let_else_max_width: 0,
312 }
313 }
314
315 pub fn set(max_width: usize) -> WidthHeuristics {
316 WidthHeuristics {
317 fn_call_width: max_width,
318 attr_fn_like_width: max_width,
319 struct_lit_width: max_width,
320 struct_variant_width: max_width,
321 array_width: max_width,
322 chain_width: max_width,
323 single_line_if_else_max_width: max_width,
324 single_line_let_else_max_width: max_width,
325 }
326 }
327
328 pub fn scaled(max_width: usize) -> WidthHeuristics {
330 const DEFAULT_MAX_WIDTH: usize = 100;
331 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
332 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
333 (ratio * 10.0).round() / 10.0
335 } else {
336 1.0
337 };
338 WidthHeuristics {
339 fn_call_width: (60.0 * max_width_ratio).round() as usize,
340 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
341 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
342 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
343 array_width: (60.0 * max_width_ratio).round() as usize,
344 chain_width: (60.0 * max_width_ratio).round() as usize,
345 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
346 single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize,
347 }
348 }
349}
350
351impl ::std::str::FromStr for WidthHeuristics {
352 type Err = &'static str;
353
354 fn from_str(_: &str) -> Result<Self, Self::Err> {
355 Err("WidthHeuristics is not parsable")
356 }
357}
358
359impl Default for EmitMode {
360 fn default() -> EmitMode {
361 EmitMode::Files
362 }
363}
364
365#[derive(Default, Clone, Debug, PartialEq)]
367pub struct IgnoreList {
368 path_set: HashSet<PathBuf>,
370 rustfmt_toml_path: PathBuf,
372}
373
374impl fmt::Display for IgnoreList {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 write!(
377 f,
378 "[{}]",
379 self.path_set
380 .iter()
381 .format_with(", ", |path, f| f(&format_args!(
382 "{}",
383 path.to_string_lossy()
384 )))
385 )
386 }
387}
388
389impl Serialize for IgnoreList {
390 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
391 where
392 S: Serializer,
393 {
394 let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
395 for e in &self.path_set {
396 seq.serialize_element(e)?;
397 }
398 seq.end()
399 }
400}
401
402impl<'de> Deserialize<'de> for IgnoreList {
403 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
404 where
405 D: Deserializer<'de>,
406 {
407 struct HashSetVisitor;
408 impl<'v> Visitor<'v> for HashSetVisitor {
409 type Value = HashSet<PathBuf>;
410
411 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
412 formatter.write_str("a sequence of path")
413 }
414
415 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
416 where
417 A: SeqAccess<'v>,
418 {
419 let mut path_set = HashSet::new();
420 while let Some(elem) = seq.next_element()? {
421 path_set.insert(elem);
422 }
423 Ok(path_set)
424 }
425 }
426 Ok(IgnoreList {
427 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
428 rustfmt_toml_path: PathBuf::new(),
429 })
430 }
431}
432
433impl<'a> IntoIterator for &'a IgnoreList {
434 type Item = &'a PathBuf;
435 type IntoIter = hash_set::Iter<'a, PathBuf>;
436
437 fn into_iter(self) -> Self::IntoIter {
438 self.path_set.iter()
439 }
440}
441
442impl IgnoreList {
443 pub fn add_prefix(&mut self, dir: &Path) {
444 self.rustfmt_toml_path = dir.to_path_buf();
445 }
446
447 pub fn rustfmt_toml_path(&self) -> &Path {
448 &self.rustfmt_toml_path
449 }
450}
451
452impl FromStr for IgnoreList {
453 type Err = &'static str;
454
455 fn from_str(_: &str) -> Result<Self, Self::Err> {
456 Err("IgnoreList is not parsable")
457 }
458}
459
460pub trait CliOptions {
463 fn apply_to(self, config: &mut Config);
464
465 fn config_path(&self) -> Option<&Path>;
468 fn edition(&self) -> Option<Edition>;
469 fn style_edition(&self) -> Option<StyleEdition>;
470 fn version(&self) -> Option<Version>;
471}
472
473#[config_type]
475pub enum Edition {
476 #[value = "2015"]
477 #[doc_hint = "2015"]
478 Edition2015,
480 #[value = "2018"]
481 #[doc_hint = "2018"]
482 Edition2018,
484 #[value = "2021"]
485 #[doc_hint = "2021"]
486 Edition2021,
488 #[value = "2024"]
489 #[doc_hint = "2024"]
490 Edition2024,
492}
493
494impl Default for Edition {
495 fn default() -> Edition {
496 Edition::Edition2015
497 }
498}
499
500impl From<Edition> for rustc_span::edition::Edition {
501 fn from(edition: Edition) -> Self {
502 match edition {
503 Edition::Edition2015 => Self::Edition2015,
504 Edition::Edition2018 => Self::Edition2018,
505 Edition::Edition2021 => Self::Edition2021,
506 Edition::Edition2024 => Self::Edition2024,
507 }
508 }
509}
510
511impl From<Edition> for StyleEdition {
512 fn from(edition: Edition) -> Self {
513 match edition {
514 Edition::Edition2015 => StyleEdition::Edition2015,
515 Edition::Edition2018 => StyleEdition::Edition2018,
516 Edition::Edition2021 => StyleEdition::Edition2021,
517 Edition::Edition2024 => StyleEdition::Edition2024,
518 }
519 }
520}
521
522impl PartialOrd for Edition {
523 fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
524 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
525 }
526}
527
528#[config_type]
530pub enum MatchArmLeadingPipe {
531 Always,
533 Never,
535 Preserve,
537}
538
539#[config_type]
545pub enum StyleEdition {
546 #[value = "2015"]
547 #[doc_hint = "2015"]
548 Edition2015,
550 #[value = "2018"]
551 #[doc_hint = "2018"]
552 Edition2018,
554 #[value = "2021"]
555 #[doc_hint = "2021"]
556 Edition2021,
558 #[value = "2024"]
559 #[doc_hint = "2024"]
560 Edition2024,
562 #[value = "2027"]
563 #[doc_hint = "2027"]
564 #[unstable_variant]
565 Edition2027,
567}
568
569impl From<StyleEdition> for rustc_span::edition::Edition {
570 fn from(edition: StyleEdition) -> Self {
571 match edition {
572 StyleEdition::Edition2015 => Self::Edition2015,
573 StyleEdition::Edition2018 => Self::Edition2018,
574 StyleEdition::Edition2021 => Self::Edition2021,
575 StyleEdition::Edition2024 => Self::Edition2024,
576 StyleEdition::Edition2027 => Self::Edition2024,
578 }
579 }
580}
581
582impl PartialOrd for StyleEdition {
583 fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
584 match (self, other) {
587 (Self::Edition2027, Self::Edition2027) => Some(std::cmp::Ordering::Equal),
588 (_, Self::Edition2027) => Some(std::cmp::Ordering::Less),
589 (Self::Edition2027, _) => Some(std::cmp::Ordering::Greater),
590 (Self::Edition2015 | Self::Edition2018 | Self::Edition2021 | Self::Edition2024, _) => {
591 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
592 }
593 }
594 }
595}
596
597#[macro_export]
599macro_rules! config_option_with_style_edition_default {
600 ($name:ident, $config_ty:ty, _ => $default:expr) => {
601 #[allow(unreachable_pub)]
602 pub struct $name;
603 $crate::style_edition_default!($name, $config_ty, _ => $default);
604 };
605 ($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
606 pub struct $name;
607 $crate::style_edition_default!(
608 $name,
609 $config_ty,
610 Edition2024 => $default_2024,
611 _ => $default_2015
612 );
613 };
614 (
615 $($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
616 $(;)*
617 ) => {
618 $(
619 config_option_with_style_edition_default!(
620 $name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
621 );
622 )*
623 };
624}
625
626config_option_with_style_edition_default!(
631 MaxWidth, usize, _ => 100;
633 HardTabs, bool, _ => false;
634 TabSpaces, usize, _ => 4;
635 NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
636 IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
637
638 UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
640 WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
641 FnCallWidth, usize, _ => 60;
642 AttrFnLikeWidth, usize, _ => 70;
643 StructLitWidth, usize, _ => 18;
644 StructVariantWidth, usize, _ => 35;
645 ArrayWidth, usize, _ => 60;
646 ChainWidth, usize, _ => 60;
647 SingleLineIfElseMaxWidth, usize, _ => 50;
648 SingleLineLetElseMaxWidth, usize, _ => 50;
649
650 WrapComments, bool, _ => false;
652 FormatCodeInDocComments, bool, _ => false;
653 DocCommentCodeBlockWidth, usize, _ => 100;
654 DocUseSmallHeuristics, DocCodeHeuristics, _ => DocCodeHeuristics::Inherit;
655 CommentWidth, usize, _ => 80;
656 NormalizeComments, bool, _ => false;
657 NormalizeDocAttributes, bool, _ => false;
658 FormatStrings, bool, _ => false;
659 FormatMacroMatchers, bool, _ => false;
660 FormatMacroBodies, bool, _ => true;
661 SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
662 HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
663 FloatLiteralTrailingZeroConfig, FloatLiteralTrailingZero, _ =>
664 FloatLiteralTrailingZero::Preserve;
665
666 EmptyItemSingleLine, bool, _ => true;
668 StructLitSingleLine, bool, _ => true;
669 FnSingleLine, bool, _ => false;
670 WhereSingleLine, bool, _ => false;
671
672 ImportsIndent, IndentStyle, _ => IndentStyle::Block;
674 ImportsLayout, ListTactic, _ => ListTactic::Mixed;
675 ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
676 GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
677 MergeImports, bool, _ => false;
678
679 ReorderImports, bool, _ => true;
681 ReorderModules, bool, _ => true;
682 ReorderImplItems, bool, _ => false;
683
684 TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
686 SpaceBeforeColon, bool, _ => false;
687 SpaceAfterColon, bool, _ => true;
688 SpacesAroundRanges, bool, _ => false;
689 BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
690
691 RemoveNestedParens, bool, _ => true;
693 CombineControlExpr, bool, _ => true;
694 ShortArrayElementWidthThreshold, usize, _ => 10;
695 OverflowDelimitedExpr, bool, _ => false;
696 StructFieldAlignThreshold, usize, _ => 0;
697 EnumDiscrimAlignThreshold, usize, _ => 0;
698 MatchArmBlocks, bool, _ => true;
699 MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
700 MatchArmIndent, bool, _ => true;
701 ForceMultilineBlocks, bool, _ => false;
702 FnArgsLayout, Density, _ => Density::Tall;
703 FnParamsLayout, Density, _ => Density::Tall;
704 BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
705 ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
706 TrailingSemicolon, bool, _ => true;
707 TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
708 MatchBlockTrailingComma, bool, _ => false;
709 BlankLinesUpperBound, usize, _ => 1;
710 BlankLinesLowerBound, usize, _ => 0;
711 EditionConfig, Edition, _ => Edition::Edition2015;
712 StyleEditionConfig, StyleEdition,
713 Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
714 VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
715 InlineAttributeWidth, usize, _ => 0;
716 FormatGeneratedFiles, bool, _ => true;
717 GeneratedMarkerLineSearchLimit, usize, _ => 5;
718
719 MergeDerives, bool, _ => true;
721 UseTryShorthand, bool, _ => false;
722 UseFieldInitShorthand, bool, _ => false;
723 ForceExplicitAbi, bool, _ => true;
724 CondenseWildcardSuffixes, bool, _ => false;
725
726 ColorConfig, Color, _ => Color::Auto;
728 RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
729 UnstableFeatures, bool, _ => false;
730 DisableAllFormatting, bool, _ => false;
731 SkipChildren, bool, _ => false;
732 HideParseErrors, bool, _ => false;
733 ShowParseErrors, bool, _ => true;
734 ErrorOnLineOverflow, bool, _ => false;
735 ErrorOnUnformatted, bool, _ => false;
736 Ignore, IgnoreList, _ => IgnoreList::default();
737
738 Verbose, Verbosity, _ => Verbosity::Normal;
740 FileLinesConfig, FileLines, _ => FileLines::all();
741 EmitModeConfig, EmitMode, _ => EmitMode::Files;
742 MakeBackup, bool, _ => false;
743 PrintMisformattedFileNames, bool, _ => false;
744);
745
746#[test]
747fn style_edition_comparisons() {
748 assert!(StyleEdition::Edition2015 == StyleEdition::Edition2015);
750 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2018);
751 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2021);
752 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2024);
753 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2027);
754
755 assert!(StyleEdition::Edition2018 > StyleEdition::Edition2015);
757 assert!(StyleEdition::Edition2018 == StyleEdition::Edition2018);
758 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2021);
759 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2024);
760 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2027);
761
762 assert!(StyleEdition::Edition2021 > StyleEdition::Edition2015);
764 assert!(StyleEdition::Edition2021 > StyleEdition::Edition2018);
765 assert!(StyleEdition::Edition2021 == StyleEdition::Edition2021);
766 assert!(StyleEdition::Edition2021 < StyleEdition::Edition2024);
767 assert!(StyleEdition::Edition2021 < StyleEdition::Edition2027);
768
769 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2015);
771 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2018);
772 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2021);
773 assert!(StyleEdition::Edition2024 == StyleEdition::Edition2024);
774 assert!(StyleEdition::Edition2024 < StyleEdition::Edition2027);
775
776 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2015);
778 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2018);
779 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2021);
780 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2024);
781 assert!(StyleEdition::Edition2027 == StyleEdition::Edition2027);
782}