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