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, len: usize) -> ListTactic {
125 match self {
126 Density::Compressed => ListTactic::Mixed,
127 Density::Tall => ListTactic::HorizontalVertical,
128 Density::Vertical if len == 1 => ListTactic::Horizontal,
129 Density::Vertical => ListTactic::Vertical,
130 }
131 }
132}
133
134#[config_type]
135pub enum GroupImportsTactic {
137 Preserve,
139 StdExternalCrate,
144 One,
146}
147
148#[config_type]
149pub enum ImportGranularity {
151 Preserve,
153 Crate,
155 Module,
157 Item,
159 One,
161}
162
163#[config_type]
165pub enum HexLiteralCase {
166 Preserve,
168 Upper,
170 Lower,
172}
173
174#[config_type]
176pub enum FloatLiteralTrailingZero {
177 Preserve,
179 Always,
181 IfNoPostfix,
184 Never,
187}
188
189#[config_type]
190pub enum ReportTactic {
191 Always,
192 Unnumbered,
193 Never,
194}
195
196#[config_type]
199pub enum EmitMode {
200 Files,
202 Stdout,
204 Coverage,
206 Checkstyle,
208 Json,
211 ModifiedLines,
213 Diff,
218}
219
220#[config_type]
222pub enum Color {
223 Always,
225 Never,
227 Auto,
229}
230
231#[config_type]
232pub enum Version {
234 One,
236 Two,
238}
239
240impl Color {
241 pub fn use_colored_tty(self) -> bool {
243 match self {
244 Color::Always | Color::Auto => true,
245 Color::Never => false,
246 }
247 }
248}
249
250#[config_type]
252pub enum Verbosity {
253 Verbose,
255 Normal,
257 Quiet,
259}
260
261#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
262pub struct WidthHeuristics {
263 pub(crate) fn_call_width: usize,
266 pub(crate) attr_fn_like_width: usize,
269 pub(crate) struct_lit_width: usize,
272 pub(crate) struct_variant_width: usize,
275 pub(crate) array_width: usize,
278 pub(crate) chain_width: usize,
280 pub(crate) single_line_if_else_max_width: usize,
283 pub(crate) single_line_let_else_max_width: usize,
286}
287
288impl fmt::Display for WidthHeuristics {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 write!(f, "{self:?}")
291 }
292}
293
294impl WidthHeuristics {
295 pub fn null() -> WidthHeuristics {
297 WidthHeuristics {
298 fn_call_width: usize::MAX,
299 attr_fn_like_width: usize::MAX,
300 struct_lit_width: 0,
301 struct_variant_width: 0,
302 array_width: usize::MAX,
303 chain_width: usize::MAX,
304 single_line_if_else_max_width: 0,
305 single_line_let_else_max_width: 0,
306 }
307 }
308
309 pub fn set(max_width: usize) -> WidthHeuristics {
310 WidthHeuristics {
311 fn_call_width: max_width,
312 attr_fn_like_width: max_width,
313 struct_lit_width: max_width,
314 struct_variant_width: max_width,
315 array_width: max_width,
316 chain_width: max_width,
317 single_line_if_else_max_width: max_width,
318 single_line_let_else_max_width: max_width,
319 }
320 }
321
322 pub fn scaled(max_width: usize) -> WidthHeuristics {
324 const DEFAULT_MAX_WIDTH: usize = 100;
325 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
326 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
327 (ratio * 10.0).round() / 10.0
329 } else {
330 1.0
331 };
332 WidthHeuristics {
333 fn_call_width: (60.0 * max_width_ratio).round() as usize,
334 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
335 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
336 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
337 array_width: (60.0 * max_width_ratio).round() as usize,
338 chain_width: (60.0 * max_width_ratio).round() as usize,
339 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
340 single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize,
341 }
342 }
343}
344
345impl ::std::str::FromStr for WidthHeuristics {
346 type Err = &'static str;
347
348 fn from_str(_: &str) -> Result<Self, Self::Err> {
349 Err("WidthHeuristics is not parsable")
350 }
351}
352
353impl Default for EmitMode {
354 fn default() -> EmitMode {
355 EmitMode::Files
356 }
357}
358
359#[derive(Default, Clone, Debug, PartialEq)]
361pub struct IgnoreList {
362 path_set: HashSet<PathBuf>,
364 rustfmt_toml_path: PathBuf,
366}
367
368impl fmt::Display for IgnoreList {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 write!(
371 f,
372 "[{}]",
373 self.path_set
374 .iter()
375 .format_with(", ", |path, f| f(&format_args!(
376 "{}",
377 path.to_string_lossy()
378 )))
379 )
380 }
381}
382
383impl Serialize for IgnoreList {
384 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
385 where
386 S: Serializer,
387 {
388 let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
389 for e in &self.path_set {
390 seq.serialize_element(e)?;
391 }
392 seq.end()
393 }
394}
395
396impl<'de> Deserialize<'de> for IgnoreList {
397 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
398 where
399 D: Deserializer<'de>,
400 {
401 struct HashSetVisitor;
402 impl<'v> Visitor<'v> for HashSetVisitor {
403 type Value = HashSet<PathBuf>;
404
405 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
406 formatter.write_str("a sequence of path")
407 }
408
409 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
410 where
411 A: SeqAccess<'v>,
412 {
413 let mut path_set = HashSet::new();
414 while let Some(elem) = seq.next_element()? {
415 path_set.insert(elem);
416 }
417 Ok(path_set)
418 }
419 }
420 Ok(IgnoreList {
421 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
422 rustfmt_toml_path: PathBuf::new(),
423 })
424 }
425}
426
427impl<'a> IntoIterator for &'a IgnoreList {
428 type Item = &'a PathBuf;
429 type IntoIter = hash_set::Iter<'a, PathBuf>;
430
431 fn into_iter(self) -> Self::IntoIter {
432 self.path_set.iter()
433 }
434}
435
436impl IgnoreList {
437 pub fn add_prefix(&mut self, dir: &Path) {
438 self.rustfmt_toml_path = dir.to_path_buf();
439 }
440
441 pub fn rustfmt_toml_path(&self) -> &Path {
442 &self.rustfmt_toml_path
443 }
444}
445
446impl FromStr for IgnoreList {
447 type Err = &'static str;
448
449 fn from_str(_: &str) -> Result<Self, Self::Err> {
450 Err("IgnoreList is not parsable")
451 }
452}
453
454pub trait CliOptions {
457 fn apply_to(self, config: &mut Config);
458
459 fn config_path(&self) -> Option<&Path>;
462 fn edition(&self) -> Option<Edition>;
463 fn style_edition(&self) -> Option<StyleEdition>;
464 fn version(&self) -> Option<Version>;
465}
466
467#[config_type]
469pub enum Edition {
470 #[value = "2015"]
471 #[doc_hint = "2015"]
472 Edition2015,
474 #[value = "2018"]
475 #[doc_hint = "2018"]
476 Edition2018,
478 #[value = "2021"]
479 #[doc_hint = "2021"]
480 Edition2021,
482 #[value = "2024"]
483 #[doc_hint = "2024"]
484 Edition2024,
486}
487
488impl Default for Edition {
489 fn default() -> Edition {
490 Edition::Edition2015
491 }
492}
493
494impl From<Edition> for rustc_span::edition::Edition {
495 fn from(edition: Edition) -> Self {
496 match edition {
497 Edition::Edition2015 => Self::Edition2015,
498 Edition::Edition2018 => Self::Edition2018,
499 Edition::Edition2021 => Self::Edition2021,
500 Edition::Edition2024 => Self::Edition2024,
501 }
502 }
503}
504
505impl From<Edition> for StyleEdition {
506 fn from(edition: Edition) -> Self {
507 match edition {
508 Edition::Edition2015 => StyleEdition::Edition2015,
509 Edition::Edition2018 => StyleEdition::Edition2018,
510 Edition::Edition2021 => StyleEdition::Edition2021,
511 Edition::Edition2024 => StyleEdition::Edition2024,
512 }
513 }
514}
515
516impl PartialOrd for Edition {
517 fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
518 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
519 }
520}
521
522#[config_type]
524pub enum MatchArmLeadingPipe {
525 Always,
527 Never,
529 Preserve,
531}
532
533#[config_type]
539pub enum StyleEdition {
540 #[value = "2015"]
541 #[doc_hint = "2015"]
542 Edition2015,
544 #[value = "2018"]
545 #[doc_hint = "2018"]
546 Edition2018,
548 #[value = "2021"]
549 #[doc_hint = "2021"]
550 Edition2021,
552 #[value = "2024"]
553 #[doc_hint = "2024"]
554 Edition2024,
556 #[value = "2027"]
557 #[doc_hint = "2027"]
558 #[unstable_variant]
559 Edition2027,
561}
562
563impl From<StyleEdition> for rustc_span::edition::Edition {
564 fn from(edition: StyleEdition) -> Self {
565 match edition {
566 StyleEdition::Edition2015 => Self::Edition2015,
567 StyleEdition::Edition2018 => Self::Edition2018,
568 StyleEdition::Edition2021 => Self::Edition2021,
569 StyleEdition::Edition2024 => Self::Edition2024,
570 StyleEdition::Edition2027 => Self::Edition2024,
572 }
573 }
574}
575
576impl PartialOrd for StyleEdition {
577 fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
578 match (self, other) {
581 (Self::Edition2027, Self::Edition2027) => Some(std::cmp::Ordering::Equal),
582 (_, Self::Edition2027) => Some(std::cmp::Ordering::Less),
583 (Self::Edition2027, _) => Some(std::cmp::Ordering::Greater),
584 (Self::Edition2015 | Self::Edition2018 | Self::Edition2021 | Self::Edition2024, _) => {
585 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
586 }
587 }
588 }
589}
590
591#[macro_export]
593macro_rules! config_option_with_style_edition_default {
594 ($name:ident, $config_ty:ty, _ => $default:expr) => {
595 #[allow(unreachable_pub)]
596 pub struct $name;
597 $crate::style_edition_default!($name, $config_ty, _ => $default);
598 };
599 ($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
600 pub struct $name;
601 $crate::style_edition_default!(
602 $name,
603 $config_ty,
604 Edition2024 => $default_2024,
605 _ => $default_2015
606 );
607 };
608 (
609 $($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
610 $(;)*
611 ) => {
612 $(
613 config_option_with_style_edition_default!(
614 $name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
615 );
616 )*
617 };
618}
619
620config_option_with_style_edition_default!(
625 MaxWidth, usize, _ => 100;
627 HardTabs, bool, _ => false;
628 TabSpaces, usize, _ => 4;
629 NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
630 IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
631
632 UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
634 WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
635 FnCallWidth, usize, _ => 60;
636 AttrFnLikeWidth, usize, _ => 70;
637 StructLitWidth, usize, _ => 18;
638 StructVariantWidth, usize, _ => 35;
639 ArrayWidth, usize, _ => 60;
640 ChainWidth, usize, _ => 60;
641 SingleLineIfElseMaxWidth, usize, _ => 50;
642 SingleLineLetElseMaxWidth, usize, _ => 50;
643
644 WrapComments, bool, _ => false;
646 FormatCodeInDocComments, bool, _ => false;
647 DocCommentCodeBlockWidth, usize, _ => 100;
648 DocUseSmallHeuristics, DocCodeHeuristics, _ => DocCodeHeuristics::Inherit;
649 CommentWidth, usize, _ => 80;
650 NormalizeComments, bool, _ => false;
651 NormalizeDocAttributes, bool, _ => false;
652 FormatStrings, bool, _ => false;
653 FormatMacroMatchers, bool, _ => false;
654 FormatMacroBodies, bool, _ => true;
655 SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
656 HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
657 FloatLiteralTrailingZeroConfig, FloatLiteralTrailingZero, _ =>
658 FloatLiteralTrailingZero::Preserve;
659
660 EmptyItemSingleLine, bool, _ => true;
662 StructLitSingleLine, bool, _ => true;
663 FnSingleLine, bool, _ => false;
664 WhereSingleLine, bool, _ => false;
665
666 ImportsIndent, IndentStyle, _ => IndentStyle::Block;
668 ImportsLayout, ListTactic, _ => ListTactic::Mixed;
669 ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
670 GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
671 MergeImports, bool, _ => false;
672
673 ReorderImports, bool, _ => true;
675 ReorderModules, bool, _ => true;
676 ReorderImplItems, bool, _ => false;
677
678 TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
680 SpaceBeforeColon, bool, _ => false;
681 SpaceAfterColon, bool, _ => true;
682 SpacesAroundRanges, bool, _ => false;
683 BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
684
685 RemoveNestedParens, bool, _ => true;
687 CombineControlExpr, bool, _ => true;
688 ShortArrayElementWidthThreshold, usize, _ => 10;
689 OverflowDelimitedExpr, bool, _ => false;
690 StructFieldAlignThreshold, usize, _ => 0;
691 EnumDiscrimAlignThreshold, usize, _ => 0;
692 MatchArmBlocks, bool, _ => true;
693 MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
694 MatchArmIndent, bool, _ => true;
695 ForceMultilineBlocks, bool, _ => false;
696 FnArgsLayout, Density, _ => Density::Tall;
697 FnParamsLayout, Density, _ => Density::Tall;
698 BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
699 ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
700 TrailingSemicolon, bool, _ => true;
701 TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
702 MatchBlockTrailingComma, bool, _ => false;
703 BlankLinesUpperBound, usize, _ => 1;
704 BlankLinesLowerBound, usize, _ => 0;
705 EditionConfig, Edition, _ => Edition::Edition2015;
706 StyleEditionConfig, StyleEdition,
707 Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
708 VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
709 InlineAttributeWidth, usize, _ => 0;
710 FormatGeneratedFiles, bool, _ => true;
711 GeneratedMarkerLineSearchLimit, usize, _ => 5;
712
713 MergeDerives, bool, _ => true;
715 UseTryShorthand, bool, _ => false;
716 UseFieldInitShorthand, bool, _ => false;
717 ForceExplicitAbi, bool, _ => true;
718 CondenseWildcardSuffixes, bool, _ => false;
719
720 ColorConfig, Color, _ => Color::Auto;
722 RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
723 UnstableFeatures, bool, _ => false;
724 DisableAllFormatting, bool, _ => false;
725 SkipChildren, bool, _ => false;
726 HideParseErrors, bool, _ => false;
727 ShowParseErrors, bool, _ => true;
728 ErrorOnLineOverflow, bool, _ => false;
729 ErrorOnUnformatted, bool, _ => false;
730 Ignore, IgnoreList, _ => IgnoreList::default();
731
732 Verbose, Verbosity, _ => Verbosity::Normal;
734 FileLinesConfig, FileLines, _ => FileLines::all();
735 EmitModeConfig, EmitMode, _ => EmitMode::Files;
736 MakeBackup, bool, _ => false;
737 PrintMisformattedFileNames, bool, _ => false;
738);
739
740#[test]
741fn style_edition_comparisons() {
742 assert!(StyleEdition::Edition2015 == StyleEdition::Edition2015);
744 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2018);
745 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2021);
746 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2024);
747 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2027);
748
749 assert!(StyleEdition::Edition2018 > StyleEdition::Edition2015);
751 assert!(StyleEdition::Edition2018 == StyleEdition::Edition2018);
752 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2021);
753 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2024);
754 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2027);
755
756 assert!(StyleEdition::Edition2021 > StyleEdition::Edition2015);
758 assert!(StyleEdition::Edition2021 > StyleEdition::Edition2018);
759 assert!(StyleEdition::Edition2021 == StyleEdition::Edition2021);
760 assert!(StyleEdition::Edition2021 < StyleEdition::Edition2024);
761 assert!(StyleEdition::Edition2021 < StyleEdition::Edition2027);
762
763 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2015);
765 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2018);
766 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2021);
767 assert!(StyleEdition::Edition2024 == StyleEdition::Edition2024);
768 assert!(StyleEdition::Edition2024 < StyleEdition::Edition2027);
769
770 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2015);
772 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2018);
773 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2021);
774 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2024);
775 assert!(StyleEdition::Edition2027 == StyleEdition::Edition2027);
776}