1use std::fmt;
9
10use clap::ValueEnum;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ValueEnum)]
14#[value(rename_all = "kebab-case")]
15pub enum GateId {
16 AdrCitesALiveRule,
18 AdrFilenameShape,
20 AdrWordCap,
22 AgentsDigestSize,
24 ChapterSizeCap,
26 ComparisonDatedTables,
28 ComparisonEscapedPipes,
30 ComparisonLegend,
32 ComparisonOneReferencePerCell,
34 ComparisonVerdictWord,
36 GateMessageCitesARule,
38 InstanceManifest,
40 KiBugzillaReportWidth,
42 KiCheckedDate,
44 KiFilenameShape,
46 KiFiling,
48 KiMechanismWalkthrough,
50 KiReportBody,
52 KiRetireWhen,
54 KiState,
56 NoPersonalPath,
58 NoSelfNarration,
60 ProseStaysUnwrapped,
62 SpecChangeIsTyped,
64 SpecRequirementParts,
66 SpecRuleIdUnique,
68 SpecSizeCap,
70 SpecVerifyHooksExist,
72 SuppressionNamesItsCase,
74 SimpleEnglish,
76 TrackingRegistry,
78}
79
80impl GateId {
81 pub const ALL: &'static [Self] = &[
83 Self::AdrCitesALiveRule,
84 Self::AdrFilenameShape,
85 Self::AdrWordCap,
86 Self::AgentsDigestSize,
87 Self::ChapterSizeCap,
88 Self::ComparisonDatedTables,
89 Self::ComparisonEscapedPipes,
90 Self::ComparisonLegend,
91 Self::ComparisonOneReferencePerCell,
92 Self::ComparisonVerdictWord,
93 Self::GateMessageCitesARule,
94 Self::InstanceManifest,
95 Self::KiBugzillaReportWidth,
96 Self::KiCheckedDate,
97 Self::KiFilenameShape,
98 Self::KiFiling,
99 Self::KiMechanismWalkthrough,
100 Self::KiReportBody,
101 Self::KiRetireWhen,
102 Self::KiState,
103 Self::NoPersonalPath,
104 Self::NoSelfNarration,
105 Self::ProseStaysUnwrapped,
106 Self::SpecChangeIsTyped,
107 Self::SpecRequirementParts,
108 Self::SpecRuleIdUnique,
109 Self::SpecSizeCap,
110 Self::SpecVerifyHooksExist,
111 Self::SuppressionNamesItsCase,
112 Self::SimpleEnglish,
113 Self::TrackingRegistry,
114 ];
115
116 #[must_use]
118 pub const fn as_str(self) -> &'static str {
119 match self {
120 Self::AdrCitesALiveRule => "adr-cites-a-live-rule",
121 Self::AdrFilenameShape => "adr-filename-shape",
122 Self::AdrWordCap => "adr-word-cap",
123 Self::AgentsDigestSize => "agents-digest-size",
124 Self::ChapterSizeCap => "chapter-size-cap",
125 Self::ComparisonDatedTables => "comparison-dated-tables",
126 Self::ComparisonEscapedPipes => "comparison-escaped-pipes",
127 Self::ComparisonLegend => "comparison-legend",
128 Self::ComparisonOneReferencePerCell => "comparison-one-reference-per-cell",
129 Self::ComparisonVerdictWord => "comparison-verdict-word",
130 Self::GateMessageCitesARule => "gate-message-cites-a-rule",
131 Self::InstanceManifest => "instance-manifest",
132 Self::KiBugzillaReportWidth => "ki-bugzilla-report-width",
133 Self::KiCheckedDate => "ki-checked-date",
134 Self::KiFilenameShape => "ki-filename-shape",
135 Self::KiFiling => "ki-filing",
136 Self::KiMechanismWalkthrough => "ki-mechanism-walkthrough",
137 Self::KiReportBody => "ki-report-body",
138 Self::KiRetireWhen => "ki-retire-when",
139 Self::KiState => "ki-state",
140 Self::NoPersonalPath => "no-personal-path",
141 Self::NoSelfNarration => "no-self-narration",
142 Self::ProseStaysUnwrapped => "prose-stays-unwrapped",
143 Self::SpecChangeIsTyped => "spec-change-is-typed",
144 Self::SpecRequirementParts => "spec-requirement-parts",
145 Self::SpecRuleIdUnique => "spec-rule-id-unique",
146 Self::SpecSizeCap => "spec-size-cap",
147 Self::SpecVerifyHooksExist => "spec-verify-hooks-exist",
148 Self::SuppressionNamesItsCase => "suppression-names-its-case",
149 Self::SimpleEnglish => "simple-english",
150 Self::TrackingRegistry => "tracking-registry",
151 }
152 }
153}
154
155impl std::str::FromStr for GateId {
156 type Err = String;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 Self::ALL
160 .iter()
161 .copied()
162 .find(|gate| gate.as_str() == s)
163 .ok_or_else(|| format!("unknown gate: {s}"))
164 }
165}
166
167impl fmt::Display for GateId {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.write_str(self.as_str())
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn all_lists_every_variant_once() {
179 let mut seen = std::collections::BTreeSet::new();
180 for gate in GateId::ALL {
181 assert!(seen.insert(gate.as_str()), "{gate} is duplicated");
182 }
183 assert_eq!(GateId::ALL.len(), 31);
184 }
185
186 #[test]
187 fn clap_value_matches_as_str() {
188 for gate in GateId::ALL {
189 let value = gate.to_possible_value().unwrap();
190 assert_eq!(value.get_name(), gate.as_str());
191 }
192 }
193}