ft_sys_shared/email/
mod.rs1#[cfg(feature = "host-only")]
2mod sqlite;
3#[cfg(feature = "host-only")]
4pub use sqlite::EmailBind;
5
6#[derive(Debug, thiserror::Error)]
7pub enum SendEmailError {
8 #[error("email not allowed: {0}")]
9 EmailNotAllowed(String),
10}
11
12#[derive(Debug, thiserror::Error)]
13pub enum CancelEmailError {
14 #[error("unknown handle")]
15 UnknownHandle,
16}
17
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
33pub struct Email {
34 pub from: EmailAddress,
35 pub to: smallvec::SmallVec<EmailAddress, 1>,
36 pub reply_to: Option<smallvec::SmallVec<EmailAddress, 1>>,
37 pub cc: smallvec::SmallVec<EmailAddress, 0>,
38 pub bcc: smallvec::SmallVec<EmailAddress, 0>,
39 pub mkind: String,
40 pub content: EmailContent,
41}
42
43impl Email {
44 pub fn merge_context(
45 &self,
46 context: Option<serde_json::Map<String, serde_json::Value>>,
47 ) -> Result<serde_json::Map<String, serde_json::Value>, serde_json::Error> {
48 let mut context = context.unwrap_or_default();
49 context.insert("from".to_string(), serde_json::to_value(&self.from)?);
50 context.insert("to".to_string(), serde_json::to_value(&self.to)?);
51 if let Some(ref reply_to) = self.reply_to {
52 context.insert("reply_to".to_string(), serde_json::to_value(reply_to)?);
53 }
54 context.insert("cc".to_string(), serde_json::to_value(&self.cc)?);
55 context.insert("bcc".to_string(), serde_json::to_value(&self.bcc)?);
56 context.insert("mkind".to_string(), serde_json::to_value(&self.mkind)?);
57 Ok(context)
58 }
59}
60
61#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
66pub enum EmailContent {
67 Rendered(RenderedEmail),
68 FromMKind {
72 context: Option<serde_json::Map<String, serde_json::Value>>,
73 },
74}
75
76#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
77pub struct RenderedEmail {
78 subject: String,
79 #[serde(rename = "html")]
80 body_html: String,
81 #[serde(rename = "text")]
82 body_text: String,
83}
84
85impl Default for EmailContent {
86 fn default() -> Self {
87 EmailContent::FromMKind { context: None }
88 }
89}
90
91impl Email {
92 pub fn new(from: EmailAddress, to: EmailAddress, mkind: &str, content: EmailContent) -> Self {
93 Email {
94 from,
95 to: smallvec::smallvec![to],
96 reply_to: None,
97 cc: smallvec::smallvec![],
98 bcc: smallvec::smallvec![],
99 mkind: mkind.to_string(),
100 content,
101 }
102 }
103}
104
105#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
106pub struct EmailAddress {
107 pub name: Option<String>,
108 pub email: String,
109}
110
111impl std::fmt::Display for EmailAddress {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 let str = match self.name {
114 Some(ref name) => format!("{name} <{}>", self.email),
115 None => self.email.to_string(),
116 };
117 write!(f, "{}", str)
118 }
119}
120
121impl From<(String, String)> for EmailAddress {
122 fn from((name, email): (String, String)) -> Self {
123 EmailAddress {
125 name: Some(name),
126 email,
127 }
128 }
129}
130
131impl From<String> for EmailAddress {
132 fn from(email: String) -> Self {
133 let email = email.trim().to_string();
134
135 if let Some(i) = email.find('<') {
137 let name = email[..i].to_string();
138 let email = email[i + 1..].to_string();
139 EmailAddress {
140 name: Some(name),
141 email,
142 }
143 } else {
144 EmailAddress { name: None, email }
145 }
146 }
147}
148
149#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
152pub struct EmailHandle(String);
153
154#[cfg(feature = "host-only")]
155impl EmailHandle {
156 #[doc(hidden)]
157 pub fn new(handle: String) -> Self {
158 Self(handle)
159 }
160
161 #[doc(hidden)]
162 pub fn inner(&self) -> &str {
163 &self.0
164 }
165}