1use std::collections::HashMap;
2
3use reqwest::Method;
4use serde::{Deserialize, Serialize};
5
6use crate::{ApiError, Client, SendRequestInput};
7
8#[derive(Clone, Debug, Default, Deserialize, Serialize)]
9#[serde(rename_all = "PascalCase")]
10pub struct Email {
11 pub from: String,
13
14 pub to: String,
16
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub cc: Option<String>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub bcc: Option<String>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub subject: Option<String>,
28
29 #[serde(flatten)]
31 pub body: Body,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub tag: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub reply_to: Option<String>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub headers: Option<Vec<Header>>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub track_opens: Option<bool>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub track_links: Option<TrackLink>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub attachments: Option<Vec<Attachment>>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub metadata: Option<HashMap<String, String>>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub message_stream: Option<String>,
64}
65
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67#[serde(untagged)]
68pub enum Body {
69 Text {
70 #[serde(rename = "TextBody")]
71 text: String,
72 },
73 Html {
74 #[serde(rename = "HtmlBody")]
75 html: String,
76 },
77 HtmlAndText {
78 #[serde(rename = "HtmlBody")]
79 html: String,
80 #[serde(rename = "TextBody")]
81 text: String,
82 },
83}
84
85impl Default for Body {
86 fn default() -> Self {
87 Body::Text {
88 text: "".into(),
89 }
90 }
91}
92
93impl Body {
94 pub fn text(text: String) -> Self {
96 Body::Text {
97 text,
98 }
99 }
100 pub fn html(html: String) -> Self {
102 Body::Html {
103 html,
104 }
105 }
106 pub fn html_and_text(html: String, text: String) -> Self {
108 Body::HtmlAndText {
109 html,
110 text,
111 }
112 }
113}
114
115#[derive(Clone, Debug, Deserialize, Serialize)]
117#[serde(rename_all = "PascalCase")]
118pub struct Header {
119 pub name: String,
120 pub value: String,
121}
122
123#[derive(Clone, Debug, Default, Deserialize, Serialize)]
125#[serde(rename_all = "PascalCase")]
126pub struct Attachment {
127 pub name: String,
128 pub content: String,
129 pub content_type: String,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub content_id: Option<String>,
132}
133
134#[derive(Clone, Debug, Deserialize, Serialize)]
136pub enum TrackLink {
137 None,
138 HtmlAndText,
139 HtmlOnly,
140 TextOnly,
141}
142
143impl Default for TrackLink {
144 fn default() -> Self {
145 Self::None
146 }
147}
148
149#[derive(Clone, Debug, Default, Deserialize, Serialize)]
150#[serde(rename_all = "PascalCase")]
151pub struct SendEmailResponse {
152 pub to: Option<String>,
153 pub submitted_at: Option<String>,
154 #[serde(rename = "MessageID")]
155 pub message_id: Option<String>,
156 pub error_code: i64,
157 pub message: String,
158}
159
160impl Client {
161 pub async fn send_email(&self, server_token: String, email: Email) -> Result<SendEmailResponse, ApiError> {
162 return self
163 .send_request(SendRequestInput {
164 method: Method::POST,
165 url: "/email".to_string(),
166 body: email,
167 server_token: Some(server_token),
168 })
169 .await;
170 }
171}