credence_lib/render/templates/
templates.rs

1use super::{super::super::configuration::*, filters::*};
2
3use {
4    ::axum::http::*,
5    bytestring::*,
6    kutil_http::*,
7    minijinja::{Error, *},
8    serde::*,
9    std::{path::*, result::Result},
10};
11
12const DEFAULT_TEMPLATE_CONTENT: &str = include_str!("default.jinja");
13
14/// Default [DateTime] format.
15pub const DEFAULT_DATE_TIME_FORMAT: &str = "[month repr:long] [day padding:none], [year]";
16
17//
18// Templates
19//
20
21/// Templates.
22#[derive(Clone, Debug)]
23pub struct Templates {
24    /// Jinja environment.
25    pub jinja_environment: Environment<'static>,
26}
27
28impl Templates {
29    /// Constructor.
30    pub fn new(configuration: &FilesConfiguration) -> Self {
31        let mut jinja_environment = Environment::new();
32        jinja_environment.set_loader(path_loader_with_default(&configuration.templates));
33        jinja_environment.set_keep_trailing_newline(true);
34        jinja_environment.set_lstrip_blocks(true);
35        jinja_environment.set_trim_blocks(true);
36
37        jinja_environment.add_global("DATE_FORMAT", DEFAULT_DATE_TIME_FORMAT);
38        minijinja_contrib::add_to_environment(&mut jinja_environment);
39
40        jinja_environment.add_global("ASSETS_PATH", configuration.assets.to_string_lossy());
41        jinja_environment.add_filter("fileversion", fileversion_filter);
42
43        Self { jinja_environment }
44    }
45
46    /// Render template.
47    pub async fn render<ContextT>(&self, template_name: &str, context: ContextT) -> Result<ByteString, StatusCode>
48    where
49        ContextT: Serialize,
50    {
51        let template = self.jinja_environment.get_template(template_name).map_err_internal_server("get template")?;
52        template.render(context).map(|string| string.into()).map_err_internal_server("render template")
53    }
54}
55
56fn path_loader_with_default<'path, PathT>(
57    path: PathT,
58) -> impl Fn(&str) -> Result<Option<String>, Error> + 'static + Send + Sync
59where
60    PathT: AsRef<Path> + 'path,
61{
62    let loader = path_loader(path);
63    move |path| {
64        loader(path).map(|template| {
65            template.or_else(|| if path == DEFAULT_TEMPLATE { Some(DEFAULT_TEMPLATE_CONTENT.into()) } else { None })
66        })
67    }
68}