1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum EventKind {
10 PullRequest,
11 Push,
12 Other,
13}
14
15impl EventKind {
16 pub fn from_header(s: &str) -> Self {
18 match s {
19 "pull_request" => Self::PullRequest,
20 "push" => Self::Push,
21 _ => Self::Other,
22 }
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "snake_case")]
29pub enum PrAction {
30 Opened,
31 Reopened,
32 Synchronize,
33 Closed,
34 #[serde(other)]
36 Other,
37}
38
39#[derive(Debug, Clone, Deserialize)]
41pub struct PullRequestEvent {
42 pub action: PrAction,
43 pub number: u64,
44 pub repository: Repository,
45 pub pull_request: PullRequest,
46}
47
48#[derive(Debug, Clone, Deserialize)]
49pub struct PullRequest {
50 pub head: Branch,
51 pub base: Branch,
52 pub draft: Option<bool>,
53 pub merged: Option<bool>,
54 #[serde(default)]
55 pub labels: Vec<Label>,
56 pub user: User,
57}
58
59#[derive(Debug, Clone, Deserialize)]
60pub struct Branch {
61 #[serde(rename = "ref")]
62 pub ref_name: String,
63 pub sha: String,
64}
65
66#[derive(Debug, Clone, Deserialize)]
67pub struct Label {
68 pub name: String,
69}
70
71#[derive(Debug, Clone, Deserialize)]
72pub struct User {
73 pub login: String,
74}
75
76#[derive(Debug, Clone, Deserialize)]
77pub struct Repository {
78 pub full_name: String, #[serde(rename = "default_branch")]
80 pub default_branch: Option<String>,
81}
82
83#[derive(Debug, Clone, Deserialize)]
85pub struct PushEvent {
86 #[serde(rename = "ref")]
87 pub ref_name: String,
88 pub after: String,
89 pub repository: Repository,
90 pub pusher: Pusher,
91}
92
93#[derive(Debug, Clone, Deserialize)]
94pub struct Pusher {
95 pub name: String,
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn event_kind_decodes_known_headers() {
104 assert_eq!(EventKind::from_header("pull_request"), EventKind::PullRequest);
105 assert_eq!(EventKind::from_header("push"), EventKind::Push);
106 assert_eq!(EventKind::from_header("ping"), EventKind::Other);
107 }
108
109 #[test]
110 fn pr_action_deserializes_known() {
111 for s in ["opened", "reopened", "synchronize", "closed"] {
112 let q = format!("\"{s}\"");
113 let action: PrAction = serde_json::from_str(&q).unwrap();
114 assert!(matches!(
115 action,
116 PrAction::Opened | PrAction::Reopened | PrAction::Synchronize | PrAction::Closed
117 ));
118 }
119 }
120
121 #[test]
122 fn pr_action_unknown_falls_through_to_other() {
123 let action: PrAction = serde_json::from_str("\"edited\"").unwrap();
124 assert_eq!(action, PrAction::Other);
125 }
126
127 #[test]
128 fn pull_request_event_parses_minimal_payload() {
129 let json = r#"{
130 "action": "opened",
131 "number": 123,
132 "repository": {
133 "full_name": "pleme-io/akeyless-deployment",
134 "default_branch": "main"
135 },
136 "pull_request": {
137 "head": { "ref": "fix-something", "sha": "abc123" },
138 "base": { "ref": "main", "sha": "def456" },
139 "draft": false,
140 "merged": false,
141 "labels": [ { "name": "needs-akeyless" } ],
142 "user": { "login": "drzln" }
143 }
144 }"#;
145 let evt: PullRequestEvent = serde_json::from_str(json).unwrap();
146 assert_eq!(evt.action, PrAction::Opened);
147 assert_eq!(evt.number, 123);
148 assert_eq!(evt.repository.full_name, "pleme-io/akeyless-deployment");
149 assert_eq!(evt.pull_request.head.ref_name, "fix-something");
150 assert_eq!(evt.pull_request.labels.len(), 1);
151 assert_eq!(evt.pull_request.labels[0].name, "needs-akeyless");
152 assert_eq!(evt.pull_request.user.login, "drzln");
153 }
154
155 #[test]
156 fn push_event_parses_minimal_payload() {
157 let json = r#"{
158 "ref": "refs/heads/main",
159 "after": "deadbeef",
160 "repository": { "full_name": "pleme-io/tatara", "default_branch": "main" },
161 "pusher": { "name": "drzln" }
162 }"#;
163 let evt: PushEvent = serde_json::from_str(json).unwrap();
164 assert_eq!(evt.ref_name, "refs/heads/main");
165 assert_eq!(evt.after, "deadbeef");
166 assert_eq!(evt.repository.full_name, "pleme-io/tatara");
167 }
168
169 #[test]
170 fn extra_fields_are_ignored() {
171 let json = r#"{
173 "action": "opened",
174 "number": 1,
175 "repository": {
176 "full_name": "x/y",
177 "default_branch": "main",
178 "forks_count": 42,
179 "open_issues": 7
180 },
181 "pull_request": {
182 "head": { "ref": "x", "sha": "y", "extra": "ignored" },
183 "base": { "ref": "x", "sha": "y" },
184 "user": { "login": "x", "site_admin": false }
185 },
186 "sender": { "anything": "here" }
187 }"#;
188 let evt: PullRequestEvent = serde_json::from_str(json).unwrap();
189 assert_eq!(evt.number, 1);
190 }
191}