use anyhow::Result;
use std::collections::HashMap;
use super::renderer::TemplateDataEnum;
use sailfish::TemplateOnce;
pub trait TemplateRenderer: Send + Sync + std::fmt::Debug {
fn name(&self) -> &'static str;
fn template_path(&self) -> &'static str;
fn render(&self, data: &TemplateDataEnum) -> Result<String>;
}
pub trait FromTemplateData: Sized {
fn from(data: TemplateDataEnum) -> Self;
}
#[derive(Debug)]
pub struct TemplateRegistry {
renderers: HashMap<String, Box<dyn TemplateRenderer>>,
}
impl TemplateRegistry {
pub fn new() -> Self {
Self {
renderers: HashMap::new(),
}
}
pub fn register(
&mut self,
template_name: &str,
renderer: Box<dyn TemplateRenderer>,
) -> Result<()> {
if self.renderers.contains_key(template_name) {
return Err(anyhow::anyhow!(
"Template '{}' is already registered",
template_name
));
}
self.renderers.insert(template_name.to_string(), renderer);
Ok(())
}
pub fn get_renderer(&self, template_name: &str) -> Option<&dyn TemplateRenderer> {
self.renderers.get(template_name).map(|r| r.as_ref())
}
pub fn has_template(&self, template_name: &str) -> bool {
self.renderers.contains_key(template_name)
}
pub fn registered_templates(&self) -> Vec<String> {
self.renderers.keys().cloned().collect()
}
}
impl Default for TemplateRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct TemplateAdapter<T> {
_phantom: std::marker::PhantomData<T>,
}
impl<T> TemplateAdapter<T> {
pub fn new() -> Self {
Self {
_phantom: std::marker::PhantomData,
}
}
}
impl<T> Default for TemplateAdapter<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> TemplateRenderer for TemplateAdapter<T>
where
T: TemplateOnce + FromTemplateData + Send + Sync + std::fmt::Debug + 'static,
{
fn name(&self) -> &'static str {
std::any::type_name::<T>()
}
fn template_path(&self) -> &'static str {
std::any::type_name::<T>()
}
fn render(&self, data: &TemplateDataEnum) -> Result<String> {
let template = T::from(data.clone());
template.render_once().map_err(|e| {
anyhow::anyhow!(
"Failed to render template '{}': {}",
std::any::type_name::<T>(),
e
)
})
}
}
#[macro_export]
macro_rules! register_template {
($registry:expr, $name:expr, $template_type:ty) => {
$registry.register(
$name,
Box::new($crate::templates::registry::TemplateAdapter::<$template_type>::new()),
)
};
}