1use anyhow::Result;
2use reqwest::{header::AUTHORIZATION, Response};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Serialize, Deserialize)]
7pub struct Mailbox {
8 pub address: String,
9 pub id: String,
10 pub alias_count: u32,
11}
12
13#[derive(Debug, Serialize, Deserialize)]
14struct MailboxDetail {
15 aliases: Vec<String>,
16}
17
18#[derive(Debug)]
20pub struct GandiEmailAPI {
21 api_key: String,
22}
23
24impl GandiEmailAPI {
25 pub fn new(api_key: String) -> Self {
27 GandiEmailAPI { api_key }
28 }
29
30 async fn get(&self, url: &str) -> Result<Response> {
31 Ok(reqwest::Client::new()
32 .get(format!("https://api.gandi.net/v5{}", url))
33 .header(AUTHORIZATION, format!("Apikey {}", self.api_key))
34 .send()
35 .await?)
36 }
37
38 async fn patch(&self, url: &str, data: &impl Serialize) -> Result<Response> {
39 Ok(reqwest::Client::new()
40 .patch(format!("https://api.gandi.net/v5{}", url))
41 .header(AUTHORIZATION, format!("Apikey {}", self.api_key))
42 .json(data)
43 .send()
44 .await?)
45 }
46
47 pub async fn domains(&self) -> Result<Vec<String>> {
49 #[derive(Debug, Serialize, Deserialize)]
50 struct Domain {
51 fqdn: String,
52 }
53
54 let res = self
55 .get("/domain/domains")
56 .await?
57 .json::<Vec<Domain>>()
58 .await?;
59 let res = res.iter().map(|r| r.fqdn.clone()).collect();
60 Ok(res)
61 }
62
63 pub async fn mailboxes(&self, domain: &str) -> Result<Vec<Mailbox>> {
65 let res = self
66 .get(&format!("/email/mailboxes/{}", domain))
67 .await?
68 .json::<Vec<Mailbox>>()
69 .await?;
70 Ok(res)
71 }
72
73 pub async fn aliases(&self, domain: &str, mailbox_id: &str) -> Result<Vec<String>> {
75 let res = self
76 .get(&format!("/email/mailboxes/{}/{}", domain, mailbox_id))
77 .await?
78 .json::<MailboxDetail>()
79 .await?;
80 Ok(res.aliases)
81 }
82
83 pub async fn create_alias(&self, domain: &str, mailbox_id: &str, alias: &str) -> Result<()> {
85 let mut aliases = self.aliases(domain, mailbox_id).await?;
86 aliases.push(alias.to_string());
87 let _res = self
88 .patch(
89 &format!("/email/mailboxes/{}/{}", domain, mailbox_id),
90 &MailboxDetail { aliases },
91 )
92 .await?;
93 Ok(())
94 }
95
96 pub async fn delete_alias(&self, domain: &str, mailbox_id: &str, alias: &str) -> Result<()> {
98 let mut aliases = self.aliases(domain, mailbox_id).await?;
99 aliases.retain(|e| e != alias);
100 let _res = self
101 .patch(
102 &format!("/email/mailboxes/{}/{}", domain, mailbox_id),
103 &MailboxDetail { aliases },
104 )
105 .await?;
106 Ok(())
107 }
108}