cratefield_module_notifications/
mail.rs1use cratefield_core::{Rendered, Template, TemplateError, TemplateRegistry};
12use cratefield_mail_templates::{self as mt, Dir, MailTheme};
13use serde::{Deserialize, Serialize};
14use serde_json::Value;
15
16pub const TEMPLATE_EMAIL: &str = "notifications/email";
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct EmailMail {
24 pub venture: String,
26 pub subject: String,
28 pub title: String,
30 pub body: String,
32 pub url: Option<String>,
34 pub lang: String,
36 pub dir: String,
38 pub unsubscribe: String,
40 pub unsubscribe_all: String,
42}
43
44struct EmailTemplate {
45 theme: Option<MailTheme>,
46}
47
48impl Template for EmailTemplate {
49 fn render(&self, data: &Value, _locale: &str) -> Result<Rendered, TemplateError> {
50 let theme = mt::theme_for_template(self.theme.as_ref(), data);
51 let data: EmailMail =
52 serde_json::from_value(data.clone()).map_err(|_| TemplateError::RenderFailed {
53 id: TEMPLATE_EMAIL.to_owned(),
54 reason: "the mail data is not the shape this template takes".to_owned(),
55 })?;
56 let mut message = mt::Message::new(&data.subject, &data.title)
57 .preheader(&data.body)
58 .lang(&data.lang)
59 .dir(if data.dir == "rtl" {
60 Dir::Rtl
61 } else {
62 Dir::Ltr
63 })
64 .paragraph(&data.body);
65 if let Some(url) = &data.url {
66 message = message.button("Open", url);
67 }
68 Ok(message
69 .footer_link("Stop receiving these", &data.unsubscribe)
70 .footer_link("stop all notification email", &data.unsubscribe_all)
71 .render(&theme)
72 .into())
73 }
74}
75
76#[must_use]
81pub fn default_templates() -> Vec<(String, Box<dyn Template>)> {
82 templates(None)
83}
84
85#[must_use]
88pub fn themed_templates(theme: &MailTheme) -> Vec<(String, Box<dyn Template>)> {
89 templates(Some(theme))
90}
91
92fn templates(theme: Option<&MailTheme>) -> Vec<(String, Box<dyn Template>)> {
93 vec![(
94 TEMPLATE_EMAIL.to_owned(),
95 Box::new(EmailTemplate {
96 theme: theme.cloned(),
97 }) as Box<dyn Template>,
98 )]
99}
100
101pub(crate) fn render(
105 registry: &TemplateRegistry,
106 data: &Value,
107 locale: &str,
108) -> Result<Rendered, TemplateError> {
109 match registry.render(TEMPLATE_EMAIL, data, locale) {
110 Err(TemplateError::UnknownTemplate { .. }) => {
111 EmailTemplate { theme: None }.render(data, locale)
112 }
113 other => other,
114 }
115}