1use base64::prelude::*;
2use serde::{Deserialize, Serialize, Serializer};
3use std::collections::HashMap;
4
5fn as_base64<T, S>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
7where
8 T: AsRef<[u8]>,
9 S: Serializer,
10{
11 serializer.serialize_str(&BASE64_STANDARD.encode(data.as_ref()))
12}
13
14#[derive(Serialize, Debug)]
15pub enum TrackLinks {
16 None,
17 HtmlTAndText,
18 HtmlOnly,
19 TextOnly,
20}
21
22#[derive(Serialize, Debug)]
23#[serde(rename_all = "PascalCase")]
24pub struct EmailHeader {
25 pub name: String,
26 pub value: String,
27}
28
29#[derive(Serialize, Debug)]
30#[serde(rename_all = "PascalCase")]
31pub struct Attachment {
32 pub name: String,
33 #[serde(serialize_with = "as_base64")]
34 pub content: Vec<u8>,
35 pub content_type: String,
36}
37
38#[derive(Serialize, Debug)]
39#[serde(untagged)]
40pub enum EmailBody {
41 #[serde(rename = "TextBody")]
42 Text(String),
43 #[serde(rename = "HtmlBody")]
44 Html(String),
45 HtmlAndText {
46 #[serde(rename = "TextBody")]
47 text: String,
48 #[serde(rename = "HtmlBody")]
49 html: String,
50 },
51}
52
53#[derive(Serialize, Debug)]
54#[serde(rename_all = "PascalCase")]
55pub struct Email {
56 pub from: String,
57 pub to: String,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub cc: Option<String>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub bcc: Option<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub subject: Option<String>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub tag: Option<String>,
66 #[serde(flatten)]
67 pub body: EmailBody,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub reply_to: Option<String>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub headers: Option<Vec<EmailHeader>>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub track_opens: Option<bool>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub track_links: Option<TrackLinks>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub metadata: Option<HashMap<String, String>>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub attachments: Option<Vec<Attachment>>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub message_stream: Option<String>,
82}
83
84impl Email {
85 pub fn new(from: String, to: String, body: EmailBody) -> Email {
86 Email {
87 from,
88 to,
89 cc: None,
90 bcc: None,
91 subject: None,
92 tag: None,
93 body,
94 reply_to: None,
95 headers: None,
96 track_opens: None,
97 track_links: None,
98 metadata: None,
99 attachments: None,
100 message_stream: None,
101 }
102 }
103}
104
105#[derive(Deserialize, Debug)]
106#[serde(rename_all = "PascalCase")]
107pub struct EmailResponse {
108 pub to: String,
109 pub submitted_at: String,
110 #[serde(rename = "MessageID")]
111 pub message_id: String,
112 pub error_code: i32,
113 pub message: String,
114}
115
116#[derive(Serialize, Debug)]
117pub enum TemplateToUse {
118 #[serde(rename = "TemplateId")]
119 Id(String),
120 #[serde(rename = "TemplateAlias")]
121 Alias(String),
122}
123
124#[derive(Serialize, Debug)]
125#[serde(rename_all = "PascalCase")]
126pub struct TemplatedEmail<M>
127where
128 M: Serialize,
129{
130 #[serde(flatten)]
131 pub template: TemplateToUse,
132 pub template_model: M,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub inline_css: Option<bool>,
135 pub from: String,
136 pub to: String,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub cc: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub bcc: Option<String>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub tag: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub reply_to: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub headers: Option<Vec<EmailHeader>>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub track_opens: Option<bool>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub track_links: Option<TrackLinks>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub metadata: Option<HashMap<String, String>>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub attachments: Option<Vec<Attachment>>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub message_stream: Option<String>,
157}
158
159impl<M> TemplatedEmail<M>
160where
161 M: Serialize,
162{
163 pub fn new(from: String, to: String, template: TemplateToUse, model: M) -> TemplatedEmail<M> {
164 TemplatedEmail {
165 template,
166 template_model: model,
167 inline_css: None,
168 from,
169 to,
170 cc: None,
171 bcc: None,
172 tag: None,
173 reply_to: None,
174 headers: None,
175 track_opens: None,
176 track_links: None,
177 metadata: None,
178 attachments: None,
179 message_stream: None,
180 }
181 }
182}
183
184#[derive(Debug, Deserialize)]
188#[serde(rename_all = "PascalCase")]
189pub struct APIError {
190 pub error_code: String,
191 pub message: String,
192}