1use std::collections::BTreeMap;
5use std::fmt;
6
7use serde::{Deserialize, Serialize};
8
9use crate::{Future, Github};
10
11#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
14pub enum WebHookContentType {
15 #[serde(rename = "json")]
17 Json,
18 #[serde(rename = "form")]
20 Form,
21}
22
23impl Default for WebHookContentType {
24 fn default() -> WebHookContentType {
25 WebHookContentType::Form
26 }
27}
28
29impl fmt::Display for WebHookContentType {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match *self {
32 WebHookContentType::Form => "form",
33 WebHookContentType::Json => "json",
34 }
35 .fmt(f)
36 }
37}
38
39pub struct Hooks {
41 github: Github,
42 owner: String,
43 repo: String,
44}
45
46impl Hooks {
47 #[doc(hidden)]
48 pub fn new<O, R>(github: Github, owner: O, repo: R) -> Self
49 where
50 O: Into<String>,
51 R: Into<String>,
52 {
53 Hooks {
54 github,
55 owner: owner.into(),
56 repo: repo.into(),
57 }
58 }
59
60 pub fn list(&self) -> Future<Vec<Hook>> {
62 self.github
63 .get(&format!("/repos/{}/{}/hooks", self.owner, self.repo))
64 }
65
66 pub fn create(&self, options: &HookCreateOptions) -> Future<Hook> {
72 self.github.post(
73 &format!("/repos/{}/{}/hooks", self.owner, self.repo),
74 json!(options),
75 )
76 }
77
78 pub fn edit(&self, id: u64, options: &HookEditOptions) -> Future<Hook> {
80 self.github.patch(
81 &format!("/repos/{}/{}/hooks/{}", self.owner, self.repo, id),
82 json!(options),
83 )
84 }
85
86 pub fn delete(&self, id: u64) -> Future<()> {
88 self.github
89 .delete(&format!("/repos/{}/{}/hooks/{}", self.owner, self.repo, id))
90 }
91}
92
93#[derive(Debug, Default, Serialize)]
99pub struct HookCreateOptions {
100 name: String,
101 config: BTreeMap<String, ::serde_json::Value>,
102 events: Vec<String>,
103 active: bool,
104}
105
106impl HookCreateOptions {
107 pub fn builder<N>(name: N) -> HookCreateOptionsBuilder
111 where
112 N: Into<String>,
113 {
114 HookCreateOptionsBuilder::new(name)
115 }
116
117 pub fn web() -> HookCreateOptionsBuilder {
119 Self::builder("web")
120 }
121}
122
123pub struct HookCreateOptionsBuilder(HookCreateOptions);
124
125impl HookCreateOptionsBuilder {
126 #[doc(hidden)]
127 pub(crate) fn new<N>(name: N) -> Self
128 where
129 N: Into<String>,
130 {
131 HookCreateOptionsBuilder(HookCreateOptions {
132 name: name.into(),
133 active: true,
134 ..Default::default()
135 })
136 }
137
138 pub fn active(&mut self, active: bool) -> &mut Self {
139 self.0.active = active;
140 self
141 }
142
143 pub fn events<E>(&mut self, events: Vec<E>) -> &mut Self
147 where
148 E: Into<String>,
149 {
150 self.0.events = events.into_iter().map(|e| e.into()).collect::<Vec<_>>();
151 self
152 }
153
154 pub fn url<U>(&mut self, url: U) -> &mut Self
156 where
157 U: Into<String>,
158 {
159 self.config_entry("url".to_owned(), ::serde_json::Value::String(url.into()))
160 }
161
162 pub fn content_type(&mut self, content_type: WebHookContentType) -> &mut Self {
165 self.config_str_entry("content_type", content_type.to_string());
166 self
167 }
168
169 pub fn secret<S>(&mut self, sec: S) -> &mut Self
172 where
173 S: Into<String>,
174 {
175 self.config_str_entry("secret", sec);
176 self
177 }
178
179 pub fn config_str_entry<K, V>(&mut self, k: K, v: V) -> &mut Self
180 where
181 K: Into<String>,
182 V: Into<String>,
183 {
184 self.config_entry(k.into(), ::serde_json::Value::String(v.into()));
185 self
186 }
187
188 pub fn config_entry<N>(&mut self, name: N, value: ::serde_json::Value) -> &mut Self
189 where
190 N: Into<String>,
191 {
192 self.0.config.insert(name.into(), value);
193 self
194 }
195
196 pub fn build(&self) -> HookCreateOptions {
197 HookCreateOptions {
198 name: self.0.name.clone(),
199 config: self.0.config.clone(),
200 events: self.0.events.clone(),
201 active: self.0.active,
202 }
203 }
204}
205
206#[derive(Debug, Default, Serialize)]
210pub struct HookEditOptions {
211 config: BTreeMap<String, ::serde_json::Value>,
212 events: Vec<String>,
213 add_events: Vec<String>,
214 remove_events: Vec<String>,
215 active: bool,
216}
217
218impl HookEditOptions {
219 pub fn builder() -> HookEditOptionsBuilder {
221 HookEditOptionsBuilder::default()
222 }
223}
224
225#[derive(Default)]
226pub struct HookEditOptionsBuilder(HookEditOptions);
227
228impl HookEditOptionsBuilder {
229 pub fn active(&mut self, active: bool) -> &mut Self {
230 self.0.active = active;
231 self
232 }
233
234 pub fn events<E>(&mut self, events: Vec<E>) -> &mut Self
238 where
239 E: Into<String>,
240 {
241 self.0.events = events.into_iter().map(|e| e.into()).collect::<Vec<_>>();
242 self
243 }
244
245 pub fn url<U>(&mut self, url: U) -> &mut Self
247 where
248 U: Into<String>,
249 {
250 self.config_entry("url".to_owned(), ::serde_json::Value::String(url.into()))
251 }
252
253 pub fn content_type(&mut self, content_type: WebHookContentType) -> &mut Self {
256 self.config_str_entry("content_type", content_type.to_string());
257 self
258 }
259
260 pub fn secret<S>(&mut self, sec: S) -> &mut Self
263 where
264 S: Into<String>,
265 {
266 self.config_str_entry("secret", sec);
267 self
268 }
269
270 pub fn config_str_entry<K, V>(&mut self, k: K, v: V) -> &mut Self
271 where
272 K: Into<String>,
273 V: Into<String>,
274 {
275 self.config_entry(k.into(), ::serde_json::Value::String(v.into()));
276 self
277 }
278
279 pub fn config_entry<N>(&mut self, name: N, value: ::serde_json::Value) -> &mut Self
280 where
281 N: Into<String>,
282 {
283 self.0.config.insert(name.into(), value);
284 self
285 }
286
287 pub fn build(&self) -> HookEditOptions {
288 HookEditOptions {
289 config: self.0.config.clone(),
290 events: self.0.events.clone(),
291 add_events: self.0.add_events.clone(),
292 remove_events: self.0.remove_events.clone(),
293 active: self.0.active,
294 }
295 }
296}
297
298#[derive(Debug, Deserialize)]
299pub struct Hook {
300 pub id: u64,
301 pub url: String,
302 pub test_url: String,
303 pub ping_url: String,
304 pub name: String,
305 pub events: Vec<String>,
306 pub config: ::serde_json::Value,
307 pub created_at: String,
308 pub updated_at: String,
309 pub active: bool,
310}
311
312impl Hook {
313 pub fn config_value(&self, name: &str) -> Option<&::serde_json::Value> {
314 self.config.pointer(&format!("/{}", name))
315 }
316
317 pub fn config_string(&self, name: &str) -> Option<String> {
318 self.config_value(name).and_then(|value| match *value {
319 ::serde_json::Value::String(ref val) => Some(val.clone()),
320 _ => None,
321 })
322 }
323
324 pub fn url(&self) -> Option<String> {
325 self.config_string("url")
326 }
327
328 pub fn content_type(&self) -> Option<String> {
329 self.config_string("content_type")
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::WebHookContentType;
336
337 #[test]
338 fn webhook_content_type_display() {
339 for (ct, expect) in &[
340 (WebHookContentType::Form, "form"),
341 (WebHookContentType::Json, "json"),
342 ] {
343 assert_eq!(ct.to_string(), *expect)
344 }
345 }
346
347 #[test]
348 fn webhook_content_type_default() {
349 let default: WebHookContentType = Default::default();
350 assert_eq!(default, WebHookContentType::Form)
351 }
352}