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 KiFilenameShape,
44 KiMechanismWalkthrough,
46 KiReportBody,
48 KiRetireWhen,
50 NoPersonalPath,
52 NoSelfNarration,
54 ProseStaysUnwrapped,
56 SpecRequirementParts,
58 SpecRuleIdUnique,
60 SpecSizeCap,
62 SpecVerifyHooksExist,
64 SuppressionNamesItsCase,
66}
67
68impl GateId {
69 pub const ALL: &'static [Self] = &[
71 Self::AdrCitesALiveRule,
72 Self::AdrFilenameShape,
73 Self::AdrWordCap,
74 Self::AgentsDigestSize,
75 Self::ChapterSizeCap,
76 Self::ComparisonDatedTables,
77 Self::ComparisonEscapedPipes,
78 Self::ComparisonLegend,
79 Self::ComparisonOneReferencePerCell,
80 Self::ComparisonVerdictWord,
81 Self::GateMessageCitesARule,
82 Self::InstanceManifest,
83 Self::KiBugzillaReportWidth,
84 Self::KiFilenameShape,
85 Self::KiMechanismWalkthrough,
86 Self::KiReportBody,
87 Self::KiRetireWhen,
88 Self::NoPersonalPath,
89 Self::NoSelfNarration,
90 Self::ProseStaysUnwrapped,
91 Self::SpecRequirementParts,
92 Self::SpecRuleIdUnique,
93 Self::SpecSizeCap,
94 Self::SpecVerifyHooksExist,
95 Self::SuppressionNamesItsCase,
96 ];
97
98 #[must_use]
100 pub const fn as_str(self) -> &'static str {
101 match self {
102 Self::AdrCitesALiveRule => "adr-cites-a-live-rule",
103 Self::AdrFilenameShape => "adr-filename-shape",
104 Self::AdrWordCap => "adr-word-cap",
105 Self::AgentsDigestSize => "agents-digest-size",
106 Self::ChapterSizeCap => "chapter-size-cap",
107 Self::ComparisonDatedTables => "comparison-dated-tables",
108 Self::ComparisonEscapedPipes => "comparison-escaped-pipes",
109 Self::ComparisonLegend => "comparison-legend",
110 Self::ComparisonOneReferencePerCell => "comparison-one-reference-per-cell",
111 Self::ComparisonVerdictWord => "comparison-verdict-word",
112 Self::GateMessageCitesARule => "gate-message-cites-a-rule",
113 Self::InstanceManifest => "instance-manifest",
114 Self::KiBugzillaReportWidth => "ki-bugzilla-report-width",
115 Self::KiFilenameShape => "ki-filename-shape",
116 Self::KiMechanismWalkthrough => "ki-mechanism-walkthrough",
117 Self::KiReportBody => "ki-report-body",
118 Self::KiRetireWhen => "ki-retire-when",
119 Self::NoPersonalPath => "no-personal-path",
120 Self::NoSelfNarration => "no-self-narration",
121 Self::ProseStaysUnwrapped => "prose-stays-unwrapped",
122 Self::SpecRequirementParts => "spec-requirement-parts",
123 Self::SpecRuleIdUnique => "spec-rule-id-unique",
124 Self::SpecSizeCap => "spec-size-cap",
125 Self::SpecVerifyHooksExist => "spec-verify-hooks-exist",
126 Self::SuppressionNamesItsCase => "suppression-names-its-case",
127 }
128 }
129}
130
131impl std::str::FromStr for GateId {
132 type Err = String;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 Self::ALL
136 .iter()
137 .copied()
138 .find(|gate| gate.as_str() == s)
139 .ok_or_else(|| format!("unknown gate: {s}"))
140 }
141}
142
143impl fmt::Display for GateId {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 f.write_str(self.as_str())
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn all_lists_every_variant_once() {
155 let mut seen = std::collections::BTreeSet::new();
156 for gate in GateId::ALL {
157 assert!(seen.insert(gate.as_str()), "{gate} is duplicated");
158 }
159 assert_eq!(GateId::ALL.len(), 25);
160 }
161
162 #[test]
163 fn clap_value_matches_as_str() {
164 for gate in GateId::ALL {
165 let value = gate.to_possible_value().unwrap();
166 assert_eq!(value.get_name(), gate.as_str());
167 }
168 }
169}