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 Unverified,
73 Stale,
75 Question,
77 Ambiguous,
80 Review,
82 Revise,
84 Stuck,
86 AutopilotDenied,
89 BugAlreadySatisfied,
91 Aside,
93 Commit,
95 Handoff,
97}
98
99impl Kind {
100 pub const ALL: [Self; 21] = [
102 Self::Unknown,
103 Self::Approval,
104 Self::ApprovalBatch,
105 Self::Ruling,
106 Self::BugConfirm,
107 Self::BugCannotReproduce,
108 Self::BugUnchangedNotHolding,
109 Self::BugReproWeak,
110 Self::Gaming,
111 Self::Unverified,
112 Self::Stale,
113 Self::Question,
114 Self::Ambiguous,
115 Self::Review,
116 Self::Revise,
117 Self::Stuck,
118 Self::AutopilotDenied,
119 Self::BugAlreadySatisfied,
120 Self::Aside,
121 Self::Commit,
122 Self::Handoff,
123 ];
124
125 #[must_use]
127 pub const fn as_str(self) -> &'static str {
128 match self {
129 Self::Unknown => "unknown",
130 Self::Approval => "approval",
131 Self::ApprovalBatch => "approval_batch",
132 Self::Ruling => "ruling",
133 Self::BugConfirm => "bug_confirm",
134 Self::BugCannotReproduce => "bug_cannot_reproduce",
135 Self::BugUnchangedNotHolding => "bug_unchanged_not_holding",
136 Self::BugReproWeak => "bug_repro_weak",
137 Self::Gaming => "gaming",
138 Self::Unverified => "unverified",
139 Self::Stale => "stale",
140 Self::Question => "question",
141 Self::Ambiguous => "ambiguous",
142 Self::Review => "review",
143 Self::Revise => "revise",
144 Self::Stuck => "stuck",
145 Self::AutopilotDenied => "autopilot_denied",
146 Self::BugAlreadySatisfied => "bug_already_satisfied",
147 Self::Aside => "aside",
148 Self::Commit => "commit",
149 Self::Handoff => "handoff",
150 }
151 }
152
153 #[must_use]
155 pub const fn group(self) -> Group {
156 match self {
157 Self::Unknown
158 | Self::Approval
159 | Self::ApprovalBatch
160 | Self::Ruling
161 | Self::BugConfirm
162 | Self::BugCannotReproduce
163 | Self::BugUnchangedNotHolding
164 | Self::BugReproWeak
165 | Self::Gaming
166 | Self::Question
167 | Self::Ambiguous
168 | Self::Revise
169 | Self::Stuck
170 | Self::Commit => Group::BlockedOnYou,
171 Self::Unverified
172 | Self::Stale
173 | Self::AutopilotDenied
174 | Self::Review
175 | Self::BugAlreadySatisfied
176 | Self::Aside
177 | Self::Handoff => Group::ReadyToReview,
178 }
179 }
180
181 #[must_use]
183 pub fn rank(self) -> usize {
184 Self::ALL
185 .iter()
186 .position(|k| *k == self)
187 .unwrap_or(Self::ALL.len())
188 }
189
190 #[must_use]
192 pub fn named(name: &str) -> Option<Self> {
193 Self::ALL.iter().copied().find(|k| k.as_str() == name)
194 }
195
196 #[must_use]
198 pub fn names() -> [&'static str; 21] {
199 Self::ALL.map(Self::as_str)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn every_kind_has_one_name_a_group_and_its_place() {
209 let schema = serde_json::to_value(schemars::schema_for!(Kind)).unwrap_or_default();
210 let mut named: Vec<String> = schema["oneOf"]
211 .as_array()
212 .into_iter()
213 .flatten()
214 .filter_map(|v| v["const"].as_str().map(str::to_owned))
215 .collect();
216 named.sort();
217 let mut listed: Vec<String> = Kind::names().iter().map(|n| (*n).to_owned()).collect();
218 listed.sort();
219 assert_eq!(named, listed, "Kind::ALL is not the enum");
220 for (i, k) in Kind::ALL.iter().enumerate() {
221 assert_eq!(k.rank(), i);
222 assert_eq!(Kind::named(k.as_str()), Some(*k));
223 assert_eq!(serde_json::to_value(k).unwrap_or_default(), k.as_str());
224 }
225 let today: Vec<&str> = Kind::names()
227 .into_iter()
228 .filter(|n| {
229 !n.starts_with("bug_")
230 && !matches!(*n, "ambiguous" | "autopilot_denied" | "unverified")
231 })
232 .collect();
233 assert_eq!(
234 today,
235 [
236 "unknown",
237 "approval",
238 "approval_batch",
239 "ruling",
240 "gaming",
241 "stale",
242 "question",
243 "review",
244 "revise",
245 "stuck",
246 "aside",
247 "commit",
248 "handoff"
249 ]
250 );
251 assert_eq!(Kind::BugAlreadySatisfied.group(), Group::ReadyToReview);
252 assert_eq!(Kind::Ambiguous.group(), Group::BlockedOnYou);
253 assert_eq!(Kind::AutopilotDenied.group(), Group::ReadyToReview);
254 assert_eq!(Kind::Unverified.group(), Group::ReadyToReview);
256 assert_eq!(Kind::Unverified.rank() + 1, Kind::Stale.rank());
257 for k in [
258 Kind::BugConfirm,
259 Kind::BugCannotReproduce,
260 Kind::BugUnchangedNotHolding,
261 Kind::BugReproWeak,
262 ] {
263 assert_eq!(k.group(), Group::BlockedOnYou, "{k:?}");
264 }
265 }
266}