1use reqwest::Method;
4use serde::{Deserialize, Serialize};
5
6use crate::{client::Sendry, error::Error, Page};
7
8#[derive(Debug, Clone)]
10pub struct Emails {
11 client: Sendry,
12}
13
14impl Emails {
15 pub(crate) fn new(client: Sendry) -> Self {
16 Self { client }
17 }
18
19 pub async fn send(&self, params: SendEmail) -> Result<EmailResponse, Error> {
21 self.client
22 .request(
23 self.client
24 .build(Method::POST, "/v1/emails", &[], Some(¶ms)),
25 )
26 .await
27 }
28
29 pub async fn send_batch(&self, params: BatchEmail) -> Result<BatchResponse, Error> {
31 self.client
32 .request(
33 self.client
34 .build(Method::POST, "/v1/emails/batch", &[], Some(¶ms)),
35 )
36 .await
37 }
38
39 pub async fn get(&self, id: &str) -> Result<Email, Error> {
41 self.client
42 .request(
43 self.client
44 .build::<()>(Method::GET, &format!("/v1/emails/{id}"), &[], None),
45 )
46 .await
47 }
48
49 pub async fn list(&self, query: ListEmails) -> Result<Page<Email>, Error> {
51 let q = query.to_query();
52 self.client
53 .request(self.client.build::<()>(Method::GET, "/v1/emails", &q, None))
54 .await
55 }
56
57 pub async fn send_marketing(
59 &self,
60 params: SendMarketingEmail,
61 ) -> Result<EmailResponse, Error> {
62 self.client
63 .request(self.client.build(
64 Method::POST,
65 "/v1/emails/marketing",
66 &[],
67 Some(¶ms),
68 ))
69 .await
70 }
71
72 pub async fn cancel(&self, id: &str) -> Result<CancelEmailResponse, Error> {
74 self.client
75 .request(self.client.build::<()>(
76 Method::POST,
77 &format!("/v1/emails/{id}/cancel"),
78 &[],
79 None,
80 ))
81 .await
82 }
83}
84
85#[derive(Debug, Clone, Default, Serialize)]
87pub struct SendMarketingEmail {
88 pub from: String,
90 pub to: Vec<String>,
92 pub subject: String,
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub html: Option<String>,
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub text: Option<String>,
100 #[serde(skip_serializing_if = "Option::is_none", rename = "reply_to")]
102 pub reply_to: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub headers: Option<serde_json::Value>,
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub tags: Option<Vec<Tag>>,
109 pub unsubscribe_url: String,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub list_id: Option<String>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub scheduled_at: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none", rename = "template_id")]
119 pub template_id: Option<String>,
120}
121
122#[derive(Debug, Clone, Deserialize)]
124pub struct CancelEmailResponse {
125 pub id: String,
127 pub status: String,
129}
130
131#[derive(Debug, Clone, Default, Serialize)]
133pub struct SendEmail {
134 pub from: String,
136 pub to: Vec<String>,
138 pub subject: String,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub html: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub text: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub cc: Option<Vec<String>>,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub bcc: Option<Vec<String>>,
152 #[serde(skip_serializing_if = "Option::is_none", rename = "reply_to")]
154 pub reply_to: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub headers: Option<serde_json::Value>,
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub tags: Option<Vec<Tag>>,
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub attachments: Option<Vec<Attachment>>,
164 #[serde(skip_serializing_if = "Option::is_none", rename = "template_id")]
166 pub template_id: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub variables: Option<serde_json::Value>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct Tag {
175 pub name: String,
177 pub value: String,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct Attachment {
184 pub filename: String,
186 pub content: String,
188 #[serde(rename = "contentType")]
190 pub content_type: String,
191}
192
193#[derive(Debug, Clone, Deserialize)]
195pub struct EmailResponse {
196 pub id: String,
198 pub status: String,
200}
201
202#[derive(Debug, Clone, Deserialize)]
204pub struct Email {
205 pub id: String,
207 pub from: String,
209 pub to: Vec<String>,
211 pub subject: String,
213 pub status: String,
215 pub created_at: String,
217}
218
219#[derive(Debug, Clone, Serialize)]
221pub struct BatchEmail {
222 pub from: String,
224 pub emails: Vec<BatchEmailItem>,
226}
227
228#[derive(Debug, Clone, Default, Serialize)]
230pub struct BatchEmailItem {
231 pub to: String,
233 pub subject: String,
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub html: Option<String>,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub text: Option<String>,
241}
242
243#[derive(Debug, Clone, Deserialize)]
245pub struct BatchResponse {
246 pub sent: u32,
248 pub failed: Vec<BatchFailure>,
250}
251
252#[derive(Debug, Clone, Deserialize)]
254pub struct BatchFailure {
255 pub to: String,
257 pub error: String,
259}
260
261#[derive(Debug, Clone, Default)]
263pub struct ListEmails {
264 pub limit: Option<u32>,
266 pub cursor: Option<String>,
268 pub status: Option<String>,
270}
271
272impl ListEmails {
273 fn to_query(&self) -> Vec<(&'static str, String)> {
274 let mut q = Vec::new();
275 if let Some(l) = self.limit {
276 q.push(("limit", l.to_string()));
277 }
278 if let Some(c) = &self.cursor {
279 q.push(("cursor", c.clone()));
280 }
281 if let Some(s) = &self.status {
282 q.push(("status", s.clone()));
283 }
284 q
285 }
286}