use include_dir::Dir;
use tera::{Context, Tera};
use crate::{errors::Error, Result};
const SUBJECT: &str = "subject.t";
const HTML: &str = "html.t";
const TEXT: &str = "text.t";
fn embedded_file(dir: &Dir<'_>, name: &str) -> Result<String> {
Ok(String::from_utf8_lossy(
dir.get_file(name)
.ok_or_else(|| Error::Message(format!("no mailer template file found {name}")))?
.contents(),
)
.to_string())
}
#[derive(Clone, Debug)]
pub struct Content {
pub subject: String,
pub text: String,
pub html: String,
}
#[derive(Debug, Clone)]
pub struct Template<'a> {
dir: &'a Dir<'a>,
}
impl<'a> Template<'a> {
pub const fn new(dir: &'a Dir<'_>) -> Self {
Self { dir }
}
pub fn render(&self, locals: &serde_json::Value) -> Result<Content> {
let subject_t = embedded_file(self.dir, SUBJECT)?;
let text_t = embedded_file(self.dir, TEXT)?;
let html_t = embedded_file(self.dir, HTML)?;
let context = Context::from_serialize(locals)?;
let text = Tera::one_off(&text_t, &context, true)?;
let html = Tera::one_off(&html_t, &context, true)?;
let subject = Tera::one_off(&subject_t, &context, true)?;
Ok(Content {
subject,
text,
html,
})
}
}
#[cfg(test)]
mod tests {
use include_dir::include_dir;
use insta::assert_debug_snapshot;
use super::*;
#[test]
fn can_render_template() {
let args = serde_json::json!({
"verifyToken": "1111-2222-3333-4444",
"name": "Can render test template",
});
assert_debug_snapshot!(
Template::new(&include_dir!("tests/fixtures/email_template/test")).render(&args)
);
}
}