1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct User {
6 pub id: i64,
7 pub login: String,
8 #[serde(rename = "type")]
9 pub user_type: Option<String>,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Repository {
15 pub id: i64,
16 pub name: String,
17 pub full_name: String,
18 pub owner: User,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct PullRequest {
24 pub number: i64,
25 pub title: String,
26 pub body: Option<String>,
27 pub user: User,
28 pub state: String,
29 pub merged: Option<bool>,
30 pub html_url: String,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Comment {
36 pub id: i64,
37 pub body: String,
38 pub user: User,
39 pub html_url: String,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct Review {
45 pub id: i64,
46 pub body: Option<String>,
47 pub user: User,
48 pub state: String,
49 pub html_url: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct Issue {
55 pub number: i64,
56 pub title: String,
57 pub user: User,
58 pub pull_request: Option<PullRequestReference>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct PullRequestReference {
64 pub url: String,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct PullRequestEvent {
70 pub action: String,
71 pub number: i64,
72 pub pull_request: PullRequest,
73 pub repository: Repository,
74 pub sender: User,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct IssueCommentEvent {
80 pub action: String,
81 pub issue: Issue,
82 pub comment: Comment,
83 pub repository: Repository,
84 pub sender: User,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct PullRequestReviewEvent {
90 pub action: String,
91 pub review: Review,
92 pub pull_request: PullRequest,
93 pub repository: Repository,
94 pub sender: User,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(rename_all = "lowercase")]
100pub enum CollaboratorRole {
101 Admin,
102 Maintain,
103 Write,
104 Triage,
105 Read,
106 None,
107}
108
109impl CollaboratorRole {
110 pub fn has_write_access(&self) -> bool {
112 matches!(self, Self::Admin | Self::Maintain | Self::Write)
113 }
114
115 pub fn is_maintainer(&self) -> bool {
117 matches!(self, Self::Admin | Self::Maintain)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_parse_pull_request_event() {
127 let json = r#"{
128 "action": "opened",
129 "number": 123,
130 "pull_request": {
131 "number": 123,
132 "title": "Test PR",
133 "body": "Test body",
134 "user": {
135 "id": 12345,
136 "login": "testuser"
137 },
138 "state": "open",
139 "merged": false,
140 "html_url": "https://github.com/owner/repo/pull/123"
141 },
142 "repository": {
143 "id": 1,
144 "name": "repo",
145 "full_name": "owner/repo",
146 "owner": {
147 "id": 1,
148 "login": "owner"
149 }
150 },
151 "sender": {
152 "id": 12345,
153 "login": "testuser"
154 }
155 }"#;
156
157 let event: PullRequestEvent = serde_json::from_str(json).unwrap();
158 assert_eq!(event.action, "opened");
159 assert_eq!(event.number, 123);
160 assert_eq!(event.pull_request.title, "Test PR");
161 assert_eq!(event.pull_request.body, Some("Test body".to_string()));
162 assert_eq!(event.pull_request.user.login, "testuser");
163 assert_eq!(event.pull_request.user.id, 12345);
164 assert_eq!(event.repository.owner.login, "owner");
165 assert_eq!(event.repository.name, "repo");
166 }
167
168 #[test]
169 fn test_parse_issue_comment_event() {
170 let json = r#"{
171 "action": "created",
172 "issue": {
173 "number": 123,
174 "title": "Test Issue",
175 "user": {
176 "id": 1,
177 "login": "owner"
178 },
179 "pull_request": {
180 "url": "https://api.github.com/repos/owner/repo/pulls/123"
181 }
182 },
183 "comment": {
184 "id": 456,
185 "body": "Test comment",
186 "user": {
187 "id": 12345,
188 "login": "testuser"
189 },
190 "html_url": "https://github.com/owner/repo/issues/123#issuecomment-456"
191 },
192 "repository": {
193 "id": 1,
194 "name": "repo",
195 "full_name": "owner/repo",
196 "owner": {
197 "id": 1,
198 "login": "owner"
199 }
200 },
201 "sender": {
202 "id": 12345,
203 "login": "testuser"
204 }
205 }"#;
206
207 let event: IssueCommentEvent = serde_json::from_str(json).unwrap();
208 assert_eq!(event.action, "created");
209 assert_eq!(event.issue.number, 123);
210 assert_eq!(event.comment.body, "Test comment");
211 assert_eq!(event.comment.user.login, "testuser");
212 assert!(event.issue.pull_request.is_some());
213 }
214
215 #[test]
216 fn test_parse_pull_request_review_event() {
217 let json = r#"{
218 "action": "submitted",
219 "review": {
220 "id": 789,
221 "body": "LGTM",
222 "user": {
223 "id": 12345,
224 "login": "testuser"
225 },
226 "state": "approved",
227 "html_url": "https://github.com/owner/repo/pull/123#pullrequestreview-789"
228 },
229 "pull_request": {
230 "number": 123,
231 "title": "Test PR",
232 "body": null,
233 "user": {
234 "id": 1,
235 "login": "owner"
236 },
237 "state": "open",
238 "merged": false,
239 "html_url": "https://github.com/owner/repo/pull/123"
240 },
241 "repository": {
242 "id": 1,
243 "name": "repo",
244 "full_name": "owner/repo",
245 "owner": {
246 "id": 1,
247 "login": "owner"
248 }
249 },
250 "sender": {
251 "id": 12345,
252 "login": "testuser"
253 }
254 }"#;
255
256 let event: PullRequestReviewEvent = serde_json::from_str(json).unwrap();
257 assert_eq!(event.action, "submitted");
258 assert_eq!(event.review.state, "approved");
259 assert_eq!(event.review.body, Some("LGTM".to_string()));
260 assert_eq!(event.review.user.login, "testuser");
261 assert_eq!(event.pull_request.number, 123);
262 }
263
264 #[test]
265 fn test_collaborator_role_has_write_access() {
266 assert!(CollaboratorRole::Admin.has_write_access());
267 assert!(CollaboratorRole::Maintain.has_write_access());
268 assert!(CollaboratorRole::Write.has_write_access());
269 assert!(!CollaboratorRole::Triage.has_write_access());
270 assert!(!CollaboratorRole::Read.has_write_access());
271 assert!(!CollaboratorRole::None.has_write_access());
272 }
273
274 #[test]
275 fn test_collaborator_role_is_maintainer() {
276 assert!(CollaboratorRole::Admin.is_maintainer());
277 assert!(CollaboratorRole::Maintain.is_maintainer());
278 assert!(!CollaboratorRole::Write.is_maintainer());
279 assert!(!CollaboratorRole::Triage.is_maintainer());
280 assert!(!CollaboratorRole::Read.is_maintainer());
281 assert!(!CollaboratorRole::None.is_maintainer());
282 }
283}