Skip to main content

meritocrab_github/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// GitHub user information
4#[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/// GitHub repository information
13#[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/// Pull request information
22#[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/// Issue comment information
34#[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/// Pull request review information
43#[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/// Pull request issue information (for comments)
53#[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/// Reference to a pull request from an issue
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct PullRequestReference {
64    pub url: String,
65}
66
67/// Pull request webhook event
68#[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/// Issue comment webhook event
78#[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/// Pull request review webhook event
88#[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/// GitHub collaborator permission level
98#[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    /// Check if role has write access or higher
111    pub fn has_write_access(&self) -> bool {
112        matches!(self, Self::Admin | Self::Maintain | Self::Write)
113    }
114
115    /// Check if role is maintainer (admin or maintain)
116    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}