polysite/compiler/
template.rs

1use crate::{builder::metadata::BODY_META, *};
2use serde_json::Value;
3use std::sync::Arc;
4use tera::Tera;
5
6/// Template engine, which uses [`Tera`].
7#[derive(Clone)]
8pub struct TemplateEngine {
9    tera: Tera,
10}
11impl TemplateEngine {
12    pub fn new(template_dir: impl AsRef<str>) -> Result<Self, Error> {
13        let tera = tera::Tera::new(template_dir.as_ref()).map_err(|err| Error::user_error(err))?;
14        Ok(Self { tera })
15    }
16
17    pub fn get(self) -> Arc<Self> {
18        Arc::new(self)
19    }
20
21    /// Render HTML using specified template and metadata
22    pub async fn render(
23        &self,
24        template: impl AsRef<str>,
25        metadata: &Metadata,
26    ) -> Result<String, Error> {
27        let tera_ctx = tera::Context::from_serialize(metadata.read_lock().await)
28            .map_err(|err| Error::user_error(err))?;
29        self.tera
30            .render(template.as_ref(), &tera_ctx)
31            .map_err(|err| Error::user_error(err))
32    }
33}
34
35/// [`TemplateRenderer`] renders HTML using the specified template and [`Metadata`] in [`Context`].
36#[derive(Clone)]
37pub struct TemplateRenderer {
38    engine: TemplateEngine,
39    template: String,
40}
41impl TemplateRenderer {
42    pub fn new(engine: TemplateEngine, template: impl AsRef<str>) -> Self {
43        let template = template.as_ref().to_owned();
44        Self { engine, template }
45    }
46}
47impl Compiler for TemplateRenderer {
48    #[tracing::instrument(skip(self, ctx))]
49    fn next_step(&mut self, mut ctx: Context) -> CompilerReturn {
50        let engine = self.engine.clone();
51        let template = self.template.clone();
52        compile!({
53            let metadata = ctx.metadata();
54            let body = engine.render(&template, &metadata).await?;
55            ctx.metadata_mut()
56                .insert_local(BODY_META.to_owned(), Value::String(body));
57            Ok(CompileStep::Completed(ctx))
58        })
59    }
60}