Skip to main content

gitee_cli_rs/api/
webhooks.rs

1use super::client::Client;
2use crate::error::{GiteeError, Result};
3use crate::models::Webhook;
4use crate::repo::Repo;
5
6pub const EVENT_FLAGS: &[&str] = &[
7    "push_events",
8    "tag_push_events",
9    "issues_events",
10    "merge_requests_events",
11    "pull_requests_events", // alias → merge_requests_events
12    "note_events",
13];
14
15pub struct Webhooks<'a> {
16    client: &'a Client,
17    repo: &'a Repo,
18}
19
20pub struct CreateWebhook<'a> {
21    pub url: &'a str,
22    pub password: Option<&'a str>,
23    pub push_events: bool,
24    pub tag_push_events: bool,
25    pub issues_events: bool,
26    pub merge_requests_events: bool,
27    pub note_events: bool,
28}
29
30/// Parse comma-separated webhook event flags from CLI `--events` values.
31/// Defaults to `push_events` when nothing is specified.
32pub fn parse_events(raw: &[String]) -> Result<Vec<String>> {
33    let joined = join_flags(raw).unwrap_or_default();
34    if joined.is_empty() {
35        return Ok(vec!["push_events".into()]);
36    }
37    let mut out = Vec::new();
38    for ev in joined.split(',') {
39        let ev = ev.trim();
40        if !EVENT_FLAGS.contains(&ev) {
41            return Err(GiteeError::Usage(format!(
42                "unknown webhook event '{ev}'; expected one of {}",
43                EVENT_FLAGS.join(", ")
44            )));
45        }
46        if !out.iter().any(|e| e == ev) {
47            out.push(ev.to_string());
48        }
49    }
50    Ok(out)
51}
52
53/// Map parsed event names to the bool fields Gitee expects on create.
54pub fn event_bools(events: &[String]) -> (bool, bool, bool, bool, bool) {
55    (
56        events.iter().any(|e| e == "push_events"),
57        events.iter().any(|e| e == "tag_push_events"),
58        events.iter().any(|e| e == "issues_events"),
59        events.iter().any(|e| {
60            e == "merge_requests_events" || e == "pull_requests_events"
61        }),
62        events.iter().any(|e| e == "note_events"),
63    )
64}
65
66fn join_flags(values: &[String]) -> Option<String> {
67    if values.is_empty() {
68        return None;
69    }
70    Some(
71        values
72            .iter()
73            .flat_map(|v| v.split(','))
74            .map(str::trim)
75            .filter(|s| !s.is_empty())
76            .collect::<Vec<_>>()
77            .join(","),
78    )
79}
80
81impl Webhooks<'_> {
82    pub(crate) fn new<'a>(client: &'a Client, repo: &'a Repo) -> Webhooks<'a> {
83        Webhooks { client, repo }
84    }
85
86    pub fn list(&self, limit: usize) -> Result<Vec<Webhook>> {
87        let (o, r) = (&self.repo.owner, &self.repo.name);
88        self.client
89            .get_paged(&format!("/repos/{o}/{r}/hooks"), &[], limit)
90    }
91
92    pub fn create(&self, req: &CreateWebhook<'_>) -> Result<Webhook> {
93        let (o, r) = (&self.repo.owner, &self.repo.name);
94        let mut form: Vec<(&str, &str)> = vec![
95            ("url", req.url),
96            ("push_events", Client::bool_str(req.push_events)),
97            ("tag_push_events", Client::bool_str(req.tag_push_events)),
98            ("issues_events", Client::bool_str(req.issues_events)),
99            (
100                "merge_requests_events",
101                Client::bool_str(req.merge_requests_events),
102            ),
103            ("note_events", Client::bool_str(req.note_events)),
104        ];
105        if let Some(password) = req.password {
106            form.push(("password", password));
107        }
108        self.client
109            .post(&format!("/repos/{o}/{r}/hooks"), &form)
110    }
111
112    pub fn delete(&self, id: i64) -> Result<()> {
113        let (o, r) = (&self.repo.owner, &self.repo.name);
114        self.client
115            .delete_ok(&format!("/repos/{o}/{r}/hooks/{id}"))
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn parse_events_defaults_to_push() {
125        assert_eq!(parse_events(&[]).unwrap(), vec!["push_events"]);
126    }
127
128    #[test]
129    fn parse_events_accepts_pull_requests_alias() {
130        let events = parse_events(&["pull_requests_events".into()]).unwrap();
131        assert_eq!(events, vec!["pull_requests_events"]);
132        let (push, tag, issues, merge, note) = event_bools(&events);
133        assert!(!push);
134        assert!(!tag);
135        assert!(!issues);
136        assert!(merge);
137        assert!(!note);
138    }
139
140    #[test]
141    fn parse_events_rejects_unknown() {
142        assert!(parse_events(&["bogus".into()]).is_err());
143    }
144}