1use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum NotificationKind {
9 ReviewRequested,
11 ChangeUpdate,
14 Impact,
16 ItemUpdate,
19 Unlock,
21 ChangeRequest,
24 RequestDigest,
27 AccessAlert,
30 Test,
32}
33
34impl NotificationKind {
35 pub const MUTABLE: [NotificationKind; 7] = [
37 NotificationKind::ReviewRequested,
38 NotificationKind::ChangeUpdate,
39 NotificationKind::Impact,
40 NotificationKind::ItemUpdate,
41 NotificationKind::Unlock,
42 NotificationKind::ChangeRequest,
43 NotificationKind::RequestDigest,
44 ];
45
46 pub fn as_str(self) -> &'static str {
47 match self {
48 NotificationKind::ReviewRequested => "review_requested",
49 NotificationKind::ChangeUpdate => "change_update",
50 NotificationKind::Impact => "impact",
51 NotificationKind::ItemUpdate => "item_update",
52 NotificationKind::Unlock => "unlock",
53 NotificationKind::ChangeRequest => "change_request",
54 NotificationKind::RequestDigest => "request_digest",
55 NotificationKind::AccessAlert => "access_alert",
56 NotificationKind::Test => "test",
57 }
58 }
59
60 pub fn label(self) -> &'static str {
61 match self {
62 NotificationKind::ReviewRequested => "Review requests",
63 NotificationKind::ChangeUpdate => "Changes I authored, review or watch",
64 NotificationKind::Impact => "Checks needed on my items (change propagation)",
65 NotificationKind::ItemUpdate => "Items I watch: new revisions, releases, check-ins",
66 NotificationKind::Unlock => "Somebody released my check-out",
67 NotificationKind::ChangeRequest => {
68 "Change requests I raised, triage, am assigned to or watch"
69 }
70 NotificationKind::RequestDigest => {
71 "E-mail digest of new change requests waiting for triage in my project groups"
72 }
73 NotificationKind::AccessAlert => "Access to a need-to-know group",
74 NotificationKind::Test => "Test messages",
75 }
76 }
77
78 pub fn parse(s: &str) -> Option<NotificationKind> {
79 [
80 NotificationKind::ReviewRequested,
81 NotificationKind::ChangeUpdate,
82 NotificationKind::Impact,
83 NotificationKind::ItemUpdate,
84 NotificationKind::Unlock,
85 NotificationKind::ChangeRequest,
86 NotificationKind::RequestDigest,
87 NotificationKind::AccessAlert,
88 NotificationKind::Test,
89 ]
90 .into_iter()
91 .find(|k| k.as_str() == s.trim())
92 }
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
102pub enum WebhookEvent {
103 #[serde(rename = "revision.released")]
104 RevisionReleased,
105 #[serde(rename = "revision.obsoleted")]
106 RevisionObsoleted,
107 #[serde(rename = "change.submitted")]
108 ChangeSubmitted,
109 #[serde(rename = "change.approved")]
110 ChangeApproved,
111 #[serde(rename = "change.rejected")]
112 ChangeRejected,
113 #[serde(rename = "change.released")]
114 ChangeReleased,
115 #[serde(rename = "change_request.created")]
116 ChangeRequestCreated,
117 #[serde(rename = "change_request.decided")]
118 ChangeRequestDecided,
119 #[serde(rename = "simulation.result_added")]
120 SimulationResultAdded,
121}
122
123impl WebhookEvent {
124 pub const ALL: [WebhookEvent; 9] = [
125 WebhookEvent::RevisionReleased,
126 WebhookEvent::RevisionObsoleted,
127 WebhookEvent::ChangeSubmitted,
128 WebhookEvent::ChangeApproved,
129 WebhookEvent::ChangeRejected,
130 WebhookEvent::ChangeReleased,
131 WebhookEvent::ChangeRequestCreated,
132 WebhookEvent::ChangeRequestDecided,
133 WebhookEvent::SimulationResultAdded,
134 ];
135
136 pub fn as_str(self) -> &'static str {
137 match self {
138 WebhookEvent::RevisionReleased => "revision.released",
139 WebhookEvent::RevisionObsoleted => "revision.obsoleted",
140 WebhookEvent::ChangeSubmitted => "change.submitted",
141 WebhookEvent::ChangeApproved => "change.approved",
142 WebhookEvent::ChangeRejected => "change.rejected",
143 WebhookEvent::ChangeReleased => "change.released",
144 WebhookEvent::ChangeRequestCreated => "change_request.created",
145 WebhookEvent::ChangeRequestDecided => "change_request.decided",
146 WebhookEvent::SimulationResultAdded => "simulation.result_added",
147 }
148 }
149
150 pub fn label(self) -> &'static str {
151 match self {
152 WebhookEvent::RevisionReleased => "A revision was released",
153 WebhookEvent::RevisionObsoleted => "A revision was made obsolete",
154 WebhookEvent::ChangeSubmitted => "A change was submitted for review",
155 WebhookEvent::ChangeApproved => "A reviewer approved a change",
156 WebhookEvent::ChangeRejected => "A change was rejected",
157 WebhookEvent::ChangeReleased => "A change was released",
158 WebhookEvent::ChangeRequestCreated => "A change request was raised",
159 WebhookEvent::ChangeRequestDecided => {
160 "A change request was accepted, rejected or deferred"
161 }
162 WebhookEvent::SimulationResultAdded => "Simulation result values were added",
163 }
164 }
165
166 pub fn parse(s: &str) -> Option<WebhookEvent> {
167 WebhookEvent::ALL
168 .into_iter()
169 .find(|e| e.as_str() == s.trim())
170 }
171}
172
173#[cfg(test)]
176mod tests {
177 use super::*;
178
179 const KINDS: [NotificationKind; 9] = [
180 NotificationKind::ReviewRequested,
181 NotificationKind::ChangeUpdate,
182 NotificationKind::Impact,
183 NotificationKind::ItemUpdate,
184 NotificationKind::Unlock,
185 NotificationKind::ChangeRequest,
186 NotificationKind::RequestDigest,
187 NotificationKind::AccessAlert,
188 NotificationKind::Test,
189 ];
190
191 #[test]
192 fn notification_kinds() {
193 let stored: Vec<&str> = KINDS.iter().map(|k| k.as_str()).collect();
194 assert_eq!(
195 stored,
196 [
197 "review_requested",
198 "change_update",
199 "impact",
200 "item_update",
201 "unlock",
202 "change_request",
203 "request_digest",
204 "access_alert",
205 "test"
206 ]
207 );
208 for k in KINDS {
209 assert_eq!(NotificationKind::parse(k.as_str()), Some(k));
210 assert_eq!(
211 NotificationKind::parse(&format!(" {} ", k.as_str())),
212 Some(k)
213 );
214 }
215 assert_eq!(NotificationKind::parse("spam"), None);
216 let mut labels: Vec<&str> = KINDS.iter().map(|k| k.label()).collect();
218 assert!(labels.iter().all(|l| l.len() > 3), "{labels:?}");
219 labels.sort_unstable();
220 labels.dedup();
221 assert_eq!(labels.len(), KINDS.len());
222 assert_eq!(NotificationKind::ReviewRequested.label(), "Review requests");
223 assert!(!NotificationKind::MUTABLE.contains(&NotificationKind::AccessAlert));
225 assert!(!NotificationKind::MUTABLE.contains(&NotificationKind::Test));
226 }
227
228 #[test]
229 fn webhook_events() {
230 let stored: Vec<&str> = WebhookEvent::ALL.iter().map(|e| e.as_str()).collect();
231 assert_eq!(
232 stored,
233 [
234 "revision.released",
235 "revision.obsoleted",
236 "change.submitted",
237 "change.approved",
238 "change.rejected",
239 "change.released",
240 "change_request.created",
241 "change_request.decided",
242 "simulation.result_added"
243 ]
244 );
245 for e in WebhookEvent::ALL {
246 assert_eq!(WebhookEvent::parse(e.as_str()), Some(e));
247 }
248 assert_eq!(WebhookEvent::parse("item.deleted"), None);
249 let mut labels: Vec<&str> = WebhookEvent::ALL.iter().map(|e| e.label()).collect();
250 assert!(labels.iter().all(|l| l.len() > 3), "{labels:?}");
251 labels.sort_unstable();
252 labels.dedup();
253 assert_eq!(labels.len(), WebhookEvent::ALL.len());
254 assert_eq!(
255 WebhookEvent::RevisionReleased.label(),
256 "A revision was released"
257 );
258 }
259}