Skip to main content

daml_fmt/
format_rules.rs

1use std::fmt;
2use std::str::FromStr;
3
4const IMPORTS: u8 = 1 << 0;
5const LAYOUT: u8 = 1 << 1;
6const SPACING: u8 = 1 << 2;
7const SYNTAX_NORMALIZATION: u8 = 1 << 3;
8const ALL: u8 = IMPORTS | LAYOUT | SPACING | SYNTAX_NORMALIZATION;
9
10/// A discrete formatter rule that can be enabled or disabled.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[non_exhaustive]
13pub enum FormatRule {
14    /// Organize import declarations into formatter-defined groups.
15    Imports,
16    /// Apply AST-guided structural layout and indentation.
17    Layout,
18    /// Normalize whitespace gaps and type-annotation colon spacing.
19    Spacing,
20    /// Rewrite layout forms into canonical multiline shapes.
21    SyntaxNormalization,
22}
23
24impl FormatRule {
25    /// Stable kebab-case CLI/config id for this formatter rule.
26    #[must_use]
27    pub const fn id(self) -> &'static str {
28        match self {
29            Self::Imports => "imports",
30            Self::Layout => "layout",
31            Self::Spacing => "spacing",
32            Self::SyntaxNormalization => "syntax-normalization",
33        }
34    }
35
36    const fn bit(self) -> u8 {
37        match self {
38            Self::Imports => IMPORTS,
39            Self::Layout => LAYOUT,
40            Self::Spacing => SPACING,
41            Self::SyntaxNormalization => SYNTAX_NORMALIZATION,
42        }
43    }
44}
45
46impl fmt::Display for FormatRule {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.write_str(self.id())
49    }
50}
51
52/// Error returned when parsing an unknown formatter rule id.
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct FormatRuleParseError {
55    value: String,
56}
57
58impl FormatRuleParseError {
59    /// Unsupported formatter rule id.
60    #[must_use]
61    pub fn value(&self) -> &str {
62        &self.value
63    }
64}
65
66impl fmt::Display for FormatRuleParseError {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(
69            f,
70            "unknown formatter rule '{}' (expected one of imports|layout|spacing|syntax-normalization)",
71            self.value
72        )
73    }
74}
75
76impl std::error::Error for FormatRuleParseError {}
77
78impl FromStr for FormatRule {
79    type Err = FormatRuleParseError;
80
81    fn from_str(value: &str) -> Result<Self, Self::Err> {
82        match value {
83            "imports" => Ok(Self::Imports),
84            "layout" => Ok(Self::Layout),
85            "spacing" => Ok(Self::Spacing),
86            "syntax-normalization" => Ok(Self::SyntaxNormalization),
87            _ => Err(FormatRuleParseError {
88                value: value.to_string(),
89            }),
90        }
91    }
92}
93
94/// A normalized set of formatter rules.
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub struct FormatRuleSet {
97    bits: u8,
98}
99
100impl Default for FormatRuleSet {
101    fn default() -> Self {
102        Self::all()
103    }
104}
105
106impl FormatRuleSet {
107    /// All formatter rules enabled.
108    #[must_use]
109    pub const fn all() -> Self {
110        Self { bits: ALL }
111    }
112
113    /// No formatter rules enabled.
114    #[must_use]
115    pub const fn none() -> Self {
116        Self { bits: 0 }
117    }
118
119    /// Build a normalized rule set from rule ids.
120    #[must_use]
121    pub fn from_rules(rules: impl IntoIterator<Item = FormatRule>) -> Self {
122        let mut set = Self::none();
123        for rule in rules {
124            set.insert(rule);
125        }
126        set
127    }
128
129    /// Enable one formatter rule.
130    pub const fn insert(&mut self, rule: FormatRule) {
131        self.bits |= rule.bit();
132    }
133
134    /// Disable one formatter rule.
135    pub const fn remove(&mut self, rule: FormatRule) {
136        self.bits &= !rule.bit();
137    }
138
139    /// Returns true when `rule` is enabled.
140    #[must_use]
141    pub const fn contains(self, rule: FormatRule) -> bool {
142        self.bits & rule.bit() != 0
143    }
144}