971c37c
use std::collections::HashMap;
use std::sync::Arc;
use maud::Markup;
use tokio::sync::RwLock;
use tracing::debug;
use crate::error::EmailError;
pub type TemplateFn = Arc<dyn Fn(&serde_json::Value) -> Markup + Send + Sync>;
#[derive(Default)]
pub struct TemplateRegistry {
inner: RwLock<HashMap<String, TemplateFn>>,
}
impl TemplateRegistry {
pub fn new() -> Self {
Self::default()
}
pub async fn register<F>(&self, name: &str, f: F)
where
F: Fn(&serde_json::Value) -> Markup + Send + Sync + 'static,
{
let mut guard = self.inner.write().await;
let replacing = guard.contains_key(name);
guard.insert(name.to_string(), Arc::new(f));
debug!(template = %name, replacing, "template registered");
}
pub async fn render(&self, name: &str, vars: &serde_json::Value) -> Result<String, EmailError> {
let guard = self.inner.read().await;
let Some(f) = guard.get(name) else {
debug!(template = %name, "template not found");
return Err(EmailError::TemplateNotFound(name.to_string()));
};
let markup = f(vars).into_string();
debug!(
template = %name,
bytes = markup.len(),
vars_bytes = vars.to_string().len(),
"template rendered"
);
Ok(markup)
}
pub async fn contains(&self, name: &str) -> bool {
let guard = self.inner.read().await;
guard.contains_key(name)
}
pub async fn names(&self) -> Vec<String> {
let guard = self.inner.read().await;
guard.keys().cloned().collect()
}
}