1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
9#[serde(rename_all = "snake_case")]
10pub enum Group {
11 BlockedOnYou,
13 ReadyToReview,
15}
16
17impl Group {
18 #[must_use]
20 pub const fn as_str(self) -> &'static str {
21 match self {
22 Self::BlockedOnYou => "blocked_on_you",
23 Self::ReadyToReview => "ready_to_review",
24 }
25 }
26
27 #[must_use]
29 pub const fn title(self) -> &'static str {
30 match self {
31 Self::BlockedOnYou => "Blocked on you",
32 Self::ReadyToReview => "Ready to review",
33 }
34 }
35}
36
37#[derive(
39 Debug,
40 Clone,
41 Copy,
42 PartialEq,
43 Eq,
44 Hash,
45 PartialOrd,
46 Ord,
47 Serialize,
48 Deserialize,
49 schemars::JsonSchema,
50)]
51#[serde(rename_all = "snake_case")]
52pub enum Kind {
53 Unknown,
55 Approval,
57 ApprovalBatch,
59 Ruling,
61 BugConfirm,
63 BugCannotReproduce,
65 BugUnchangedNotHolding,
67 BugReproWeak,
69 Gaming,
71 Stale,
73 Question,
75 Ambiguous,
78 Review,
80 Revise,
82 Stuck,
84 AutopilotDenied,
87 BugAlreadySatisfied,
89 Aside,
91 Commit,
93 Handoff,
95}
96
97impl Kind {
98 pub const ALL: [Self; 20] = [
100 Self::Unknown,
101 Self::Approval,
102 Self::ApprovalBatch,
103 Self::Ruling,
104 Self::BugConfirm,
105 Self::BugCannotReproduce,
106 Self::BugUnchangedNotHolding,
107 Self::BugReproWeak,
108 Self::Gaming,
109 Self::Stale,
110 Self::Question,
111 Self::Ambiguous,
112 Self::Review,
113 Self::Revise,
114 Self::Stuck,
115 Self::AutopilotDenied,
116 Self::BugAlreadySatisfied,
117 Self::Aside,
118 Self::Commit,
119 Self::Handoff,
120 ];
121
122 #[must_use]
124 pub const fn as_str(self) -> &'static str {
125 match self {
126 Self::Unknown => "unknown",
127 Self::Approval => "approval",
128 Self::ApprovalBatch => "approval_batch",
129 Self::Ruling => "ruling",
130 Self::BugConfirm => "bug_confirm",
131 Self::BugCannotReproduce => "bug_cannot_reproduce",
132 Self::BugUnchangedNotHolding => "bug_unchanged_not_holding",
133 Self::BugReproWeak => "bug_repro_weak",
134 Self::Gaming => "gaming",
135 Self::Stale => "stale",
136 Self::Question => "question",
137 Self::Ambiguous => "ambiguous",
138 Self::Review => "review",
139 Self::Revise => "revise",
140 Self::Stuck => "stuck",
141 Self::AutopilotDenied => "autopilot_denied",
142 Self::BugAlreadySatisfied => "bug_already_satisfied",
143 Self::Aside => "aside",
144 Self::Commit => "commit",
145 Self::Handoff => "handoff",
146 }
147 }
148
149 #[must_use]
151 pub const fn group(self) -> Group {
152 match self {
153 Self::Unknown
154 | Self::Approval
155 | Self::ApprovalBatch
156 | Self::Ruling
157 | Self::BugConfirm
158 | Self::BugCannotReproduce
159 | Self::BugUnchangedNotHolding
160 | Self::BugReproWeak
161 | Self::Gaming
162 | Self::Question
163 | Self::Ambiguous
164 | Self::Revise
165 | Self::Stuck
166 | Self::Commit => Group::BlockedOnYou,
167 Self::Stale
168 | Self::AutopilotDenied
169 | Self::Review
170 | Self::BugAlreadySatisfied
171 | Self::Aside
172 | Self::Handoff => Group::ReadyToReview,
173 }
174 }
175
176 #[must_use]
178 pub fn rank(self) -> usize {
179 Self::ALL
180 .iter()
181 .position(|k| *k == self)
182 .unwrap_or(Self::ALL.len())
183 }
184
185 #[must_use]
187 pub fn named(name: &str) -> Option<Self> {
188 Self::ALL.iter().copied().find(|k| k.as_str() == name)
189 }
190
191 #[must_use]
193 pub fn names() -> [&'static str; 20] {
194 Self::ALL.map(Self::as_str)
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn every_kind_has_one_name_a_group_and_its_place() {
204 let schema = serde_json::to_value(schemars::schema_for!(Kind)).unwrap_or_default();
205 let mut named: Vec<String> = schema["oneOf"]
206 .as_array()
207 .into_iter()
208 .flatten()
209 .filter_map(|v| v["const"].as_str().map(str::to_owned))
210 .collect();
211 named.sort();
212 let mut listed: Vec<String> = Kind::names().iter().map(|n| (*n).to_owned()).collect();
213 listed.sort();
214 assert_eq!(named, listed, "Kind::ALL is not the enum");
215 for (i, k) in Kind::ALL.iter().enumerate() {
216 assert_eq!(k.rank(), i);
217 assert_eq!(Kind::named(k.as_str()), Some(*k));
218 assert_eq!(serde_json::to_value(k).unwrap_or_default(), k.as_str());
219 }
220 let today: Vec<&str> = Kind::names()
222 .into_iter()
223 .filter(|n| !n.starts_with("bug_") && !matches!(*n, "ambiguous" | "autopilot_denied"))
224 .collect();
225 assert_eq!(
226 today,
227 [
228 "unknown",
229 "approval",
230 "approval_batch",
231 "ruling",
232 "gaming",
233 "stale",
234 "question",
235 "review",
236 "revise",
237 "stuck",
238 "aside",
239 "commit",
240 "handoff"
241 ]
242 );
243 assert_eq!(Kind::BugAlreadySatisfied.group(), Group::ReadyToReview);
244 assert_eq!(Kind::Ambiguous.group(), Group::BlockedOnYou);
245 assert_eq!(Kind::AutopilotDenied.group(), Group::ReadyToReview);
246 for k in [
247 Kind::BugConfirm,
248 Kind::BugCannotReproduce,
249 Kind::BugUnchangedNotHolding,
250 Kind::BugReproWeak,
251 ] {
252 assert_eq!(k.group(), Group::BlockedOnYou, "{k:?}");
253 }
254 }
255}