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