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
98impl Density {
99 pub fn to_list_tactic(self, len: usize) -> ListTactic {
100 match self {
101 Density::Compressed => ListTactic::Mixed,
102 Density::Tall => ListTactic::HorizontalVertical,
103 Density::Vertical if len == 1 => ListTactic::Horizontal,
104 Density::Vertical => ListTactic::Vertical,
105 }
106 }
107}
108
109#[config_type]
110pub enum GroupImportsTactic {
112 Preserve,
114 StdExternalCrate,
119 One,
121}
122
123#[config_type]
124pub enum ImportGranularity {
126 Preserve,
128 Crate,
130 Module,
132 Item,
134 One,
136}
137
138#[config_type]
140pub enum HexLiteralCase {
141 Preserve,
143 Upper,
145 Lower,
147}
148
149#[config_type]
151pub enum FloatLiteralTrailingZero {
152 Preserve,
154 Always,
156 IfNoPostfix,
159 Never,
162}
163
164#[config_type]
165pub enum ReportTactic {
166 Always,
167 Unnumbered,
168 Never,
169}
170
171#[config_type]
174pub enum EmitMode {
175 Files,
177 Stdout,
179 Coverage,
181 Checkstyle,
183 Json,
186 ModifiedLines,
188 Diff,
193}
194
195#[config_type]
197pub enum Color {
198 Always,
200 Never,
202 Auto,
204}
205
206#[config_type]
207pub enum Version {
209 One,
211 Two,
213}
214
215impl Color {
216 pub fn use_colored_tty(self) -> bool {
218 match self {
219 Color::Always | Color::Auto => true,
220 Color::Never => false,
221 }
222 }
223}
224
225#[config_type]
227pub enum Verbosity {
228 Verbose,
230 Normal,
232 Quiet,
234}
235
236#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
237pub struct WidthHeuristics {
238 pub(crate) fn_call_width: usize,
241 pub(crate) attr_fn_like_width: usize,
244 pub(crate) struct_lit_width: usize,
247 pub(crate) struct_variant_width: usize,
250 pub(crate) array_width: usize,
253 pub(crate) chain_width: usize,
255 pub(crate) single_line_if_else_max_width: usize,
258 pub(crate) single_line_let_else_max_width: usize,
261}
262
263impl fmt::Display for WidthHeuristics {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 write!(f, "{self:?}")
266 }
267}
268
269impl WidthHeuristics {
270 pub fn null() -> WidthHeuristics {
272 WidthHeuristics {
273 fn_call_width: usize::MAX,
274 attr_fn_like_width: usize::MAX,
275 struct_lit_width: 0,
276 struct_variant_width: 0,
277 array_width: usize::MAX,
278 chain_width: usize::MAX,
279 single_line_if_else_max_width: 0,
280 single_line_let_else_max_width: 0,
281 }
282 }
283
284 pub fn set(max_width: usize) -> WidthHeuristics {
285 WidthHeuristics {
286 fn_call_width: max_width,
287 attr_fn_like_width: max_width,
288 struct_lit_width: max_width,
289 struct_variant_width: max_width,
290 array_width: max_width,
291 chain_width: max_width,
292 single_line_if_else_max_width: max_width,
293 single_line_let_else_max_width: max_width,
294 }
295 }
296
297 pub fn scaled(max_width: usize) -> WidthHeuristics {
299 const DEFAULT_MAX_WIDTH: usize = 100;
300 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
301 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
302 (ratio * 10.0).round() / 10.0
304 } else {
305 1.0
306 };
307 WidthHeuristics {
308 fn_call_width: (60.0 * max_width_ratio).round() as usize,
309 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
310 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
311 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
312 array_width: (60.0 * max_width_ratio).round() as usize,
313 chain_width: (60.0 * max_width_ratio).round() as usize,
314 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
315 single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize,
316 }
317 }
318}
319
320impl ::std::str::FromStr for WidthHeuristics {
321 type Err = &'static str;
322
323 fn from_str(_: &str) -> Result<Self, Self::Err> {
324 Err("WidthHeuristics is not parsable")
325 }
326}
327
328impl Default for EmitMode {
329 fn default() -> EmitMode {
330 EmitMode::Files
331 }
332}
333
334#[derive(Default, Clone, Debug, PartialEq)]
336pub struct IgnoreList {
337 path_set: HashSet<PathBuf>,
339 rustfmt_toml_path: PathBuf,
341}
342
343impl fmt::Display for IgnoreList {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 write!(
346 f,
347 "[{}]",
348 self.path_set
349 .iter()
350 .format_with(", ", |path, f| f(&format_args!(
351 "{}",
352 path.to_string_lossy()
353 )))
354 )
355 }
356}
357
358impl Serialize for IgnoreList {
359 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
360 where
361 S: Serializer,
362 {
363 let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
364 for e in &self.path_set {
365 seq.serialize_element(e)?;
366 }
367 seq.end()
368 }
369}
370
371impl<'de> Deserialize<'de> for IgnoreList {
372 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
373 where
374 D: Deserializer<'de>,
375 {
376 struct HashSetVisitor;
377 impl<'v> Visitor<'v> for HashSetVisitor {
378 type Value = HashSet<PathBuf>;
379
380 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
381 formatter.write_str("a sequence of path")
382 }
383
384 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
385 where
386 A: SeqAccess<'v>,
387 {
388 let mut path_set = HashSet::new();
389 while let Some(elem) = seq.next_element()? {
390 path_set.insert(elem);
391 }
392 Ok(path_set)
393 }
394 }
395 Ok(IgnoreList {
396 path_set: deserializer.deserialize_seq(HashSetVisitor)?,
397 rustfmt_toml_path: PathBuf::new(),
398 })
399 }
400}
401
402impl<'a> IntoIterator for &'a IgnoreList {
403 type Item = &'a PathBuf;
404 type IntoIter = hash_set::Iter<'a, PathBuf>;
405
406 fn into_iter(self) -> Self::IntoIter {
407 self.path_set.iter()
408 }
409}
410
411impl IgnoreList {
412 pub fn add_prefix(&mut self, dir: &Path) {
413 self.rustfmt_toml_path = dir.to_path_buf();
414 }
415
416 pub fn rustfmt_toml_path(&self) -> &Path {
417 &self.rustfmt_toml_path
418 }
419}
420
421impl FromStr for IgnoreList {
422 type Err = &'static str;
423
424 fn from_str(_: &str) -> Result<Self, Self::Err> {
425 Err("IgnoreList is not parsable")
426 }
427}
428
429pub trait CliOptions {
432 fn apply_to(self, config: &mut Config);
433
434 fn config_path(&self) -> Option<&Path>;
437 fn edition(&self) -> Option<Edition>;
438 fn style_edition(&self) -> Option<StyleEdition>;
439 fn version(&self) -> Option<Version>;
440}
441
442#[config_type]
444pub enum Edition {
445 #[value = "2015"]
446 #[doc_hint = "2015"]
447 Edition2015,
449 #[value = "2018"]
450 #[doc_hint = "2018"]
451 Edition2018,
453 #[value = "2021"]
454 #[doc_hint = "2021"]
455 Edition2021,
457 #[value = "2024"]
458 #[doc_hint = "2024"]
459 Edition2024,
461}
462
463impl Default for Edition {
464 fn default() -> Edition {
465 Edition::Edition2015
466 }
467}
468
469impl From<Edition> for rustc_span::edition::Edition {
470 fn from(edition: Edition) -> Self {
471 match edition {
472 Edition::Edition2015 => Self::Edition2015,
473 Edition::Edition2018 => Self::Edition2018,
474 Edition::Edition2021 => Self::Edition2021,
475 Edition::Edition2024 => Self::Edition2024,
476 }
477 }
478}
479
480impl From<Edition> for StyleEdition {
481 fn from(edition: Edition) -> Self {
482 match edition {
483 Edition::Edition2015 => StyleEdition::Edition2015,
484 Edition::Edition2018 => StyleEdition::Edition2018,
485 Edition::Edition2021 => StyleEdition::Edition2021,
486 Edition::Edition2024 => StyleEdition::Edition2024,
487 }
488 }
489}
490
491impl PartialOrd for Edition {
492 fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
493 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
494 }
495}
496
497#[config_type]
499pub enum MatchArmLeadingPipe {
500 Always,
502 Never,
504 Preserve,
506}
507
508#[config_type]
514pub enum StyleEdition {
515 #[value = "2015"]
516 #[doc_hint = "2015"]
517 Edition2015,
519 #[value = "2018"]
520 #[doc_hint = "2018"]
521 Edition2018,
523 #[value = "2021"]
524 #[doc_hint = "2021"]
525 Edition2021,
527 #[value = "2024"]
528 #[doc_hint = "2024"]
529 Edition2024,
531 #[value = "2027"]
532 #[doc_hint = "2027"]
533 #[unstable_variant]
534 Edition2027,
536}
537
538impl From<StyleEdition> for rustc_span::edition::Edition {
539 fn from(edition: StyleEdition) -> Self {
540 match edition {
541 StyleEdition::Edition2015 => Self::Edition2015,
542 StyleEdition::Edition2018 => Self::Edition2018,
543 StyleEdition::Edition2021 => Self::Edition2021,
544 StyleEdition::Edition2024 => Self::Edition2024,
545 StyleEdition::Edition2027 => Self::Edition2024,
547 }
548 }
549}
550
551impl PartialOrd for StyleEdition {
552 fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
553 match (self, other) {
556 (Self::Edition2027, Self::Edition2027) => Some(std::cmp::Ordering::Equal),
557 (_, Self::Edition2027) => Some(std::cmp::Ordering::Less),
558 (Self::Edition2027, _) => Some(std::cmp::Ordering::Greater),
559 (Self::Edition2015 | Self::Edition2018 | Self::Edition2021 | Self::Edition2024, _) => {
560 rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
561 }
562 }
563 }
564}
565
566#[macro_export]
568macro_rules! config_option_with_style_edition_default {
569 ($name:ident, $config_ty:ty, _ => $default:expr) => {
570 #[allow(unreachable_pub)]
571 pub struct $name;
572 $crate::style_edition_default!($name, $config_ty, _ => $default);
573 };
574 ($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
575 pub struct $name;
576 $crate::style_edition_default!(
577 $name,
578 $config_ty,
579 Edition2024 => $default_2024,
580 _ => $default_2015
581 );
582 };
583 (
584 $($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
585 $(;)*
586 ) => {
587 $(
588 config_option_with_style_edition_default!(
589 $name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
590 );
591 )*
592 };
593}
594
595config_option_with_style_edition_default!(
600 MaxWidth, usize, _ => 100;
602 HardTabs, bool, _ => false;
603 TabSpaces, usize, _ => 4;
604 NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
605 IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
606
607 UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
609 WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
610 FnCallWidth, usize, _ => 60;
611 AttrFnLikeWidth, usize, _ => 70;
612 StructLitWidth, usize, _ => 18;
613 StructVariantWidth, usize, _ => 35;
614 ArrayWidth, usize, _ => 60;
615 ChainWidth, usize, _ => 60;
616 SingleLineIfElseMaxWidth, usize, _ => 50;
617 SingleLineLetElseMaxWidth, usize, _ => 50;
618
619 WrapComments, bool, _ => false;
621 FormatCodeInDocComments, bool, _ => false;
622 DocCommentCodeBlockWidth, usize, _ => 100;
623 CommentWidth, usize, _ => 80;
624 NormalizeComments, bool, _ => false;
625 NormalizeDocAttributes, bool, _ => false;
626 FormatStrings, bool, _ => false;
627 FormatMacroMatchers, bool, _ => false;
628 FormatMacroBodies, bool, _ => true;
629 SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
630 HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
631 FloatLiteralTrailingZeroConfig, FloatLiteralTrailingZero, _ =>
632 FloatLiteralTrailingZero::Preserve;
633
634 EmptyItemSingleLine, bool, _ => true;
636 StructLitSingleLine, bool, _ => true;
637 FnSingleLine, bool, _ => false;
638 WhereSingleLine, bool, _ => false;
639
640 ImportsIndent, IndentStyle, _ => IndentStyle::Block;
642 ImportsLayout, ListTactic, _ => ListTactic::Mixed;
643 ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
644 GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
645 MergeImports, bool, _ => false;
646
647 ReorderImports, bool, _ => true;
649 ReorderModules, bool, _ => true;
650 ReorderImplItems, bool, _ => false;
651
652 TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
654 SpaceBeforeColon, bool, _ => false;
655 SpaceAfterColon, bool, _ => true;
656 SpacesAroundRanges, bool, _ => false;
657 BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
658
659 RemoveNestedParens, bool, _ => true;
661 CombineControlExpr, bool, _ => true;
662 ShortArrayElementWidthThreshold, usize, _ => 10;
663 OverflowDelimitedExpr, bool, _ => false;
664 StructFieldAlignThreshold, usize, _ => 0;
665 EnumDiscrimAlignThreshold, usize, _ => 0;
666 MatchArmBlocks, bool, _ => true;
667 MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
668 MatchArmIndent, bool, _ => true;
669 ForceMultilineBlocks, bool, _ => false;
670 FnArgsLayout, Density, _ => Density::Tall;
671 FnParamsLayout, Density, _ => Density::Tall;
672 BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
673 ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
674 TrailingSemicolon, bool, _ => true;
675 TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
676 MatchBlockTrailingComma, bool, _ => false;
677 BlankLinesUpperBound, usize, _ => 1;
678 BlankLinesLowerBound, usize, _ => 0;
679 EditionConfig, Edition, _ => Edition::Edition2015;
680 StyleEditionConfig, StyleEdition,
681 Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
682 VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
683 InlineAttributeWidth, usize, _ => 0;
684 FormatGeneratedFiles, bool, _ => true;
685 GeneratedMarkerLineSearchLimit, usize, _ => 5;
686
687 MergeDerives, bool, _ => true;
689 UseTryShorthand, bool, _ => false;
690 UseFieldInitShorthand, bool, _ => false;
691 ForceExplicitAbi, bool, _ => true;
692 CondenseWildcardSuffixes, bool, _ => false;
693
694 ColorConfig, Color, _ => Color::Auto;
696 RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
697 UnstableFeatures, bool, _ => false;
698 DisableAllFormatting, bool, _ => false;
699 SkipChildren, bool, _ => false;
700 HideParseErrors, bool, _ => false;
701 ShowParseErrors, bool, _ => true;
702 ErrorOnLineOverflow, bool, _ => false;
703 ErrorOnUnformatted, bool, _ => false;
704 Ignore, IgnoreList, _ => IgnoreList::default();
705
706 Verbose, Verbosity, _ => Verbosity::Normal;
708 FileLinesConfig, FileLines, _ => FileLines::all();
709 EmitModeConfig, EmitMode, _ => EmitMode::Files;
710 MakeBackup, bool, _ => false;
711 PrintMisformattedFileNames, bool, _ => false;
712);
713
714#[test]
715fn style_edition_comparisons() {
716 assert!(StyleEdition::Edition2015 == StyleEdition::Edition2015);
718 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2018);
719 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2021);
720 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2024);
721 assert!(StyleEdition::Edition2015 < StyleEdition::Edition2027);
722
723 assert!(StyleEdition::Edition2018 > StyleEdition::Edition2015);
725 assert!(StyleEdition::Edition2018 == StyleEdition::Edition2018);
726 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2021);
727 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2024);
728 assert!(StyleEdition::Edition2018 < StyleEdition::Edition2027);
729
730 assert!(StyleEdition::Edition2021 > StyleEdition::Edition2015);
732 assert!(StyleEdition::Edition2021 > StyleEdition::Edition2018);
733 assert!(StyleEdition::Edition2021 == StyleEdition::Edition2021);
734 assert!(StyleEdition::Edition2021 < StyleEdition::Edition2024);
735 assert!(StyleEdition::Edition2021 < StyleEdition::Edition2027);
736
737 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2015);
739 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2018);
740 assert!(StyleEdition::Edition2024 > StyleEdition::Edition2021);
741 assert!(StyleEdition::Edition2024 == StyleEdition::Edition2024);
742 assert!(StyleEdition::Edition2024 < StyleEdition::Edition2027);
743
744 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2015);
746 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2018);
747 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2021);
748 assert!(StyleEdition::Edition2027 > StyleEdition::Edition2024);
749 assert!(StyleEdition::Edition2027 == StyleEdition::Edition2027);
750}