jinja_renderer/
lib.rs

1mod frameworks;
2
3#[cfg(feature = "minify")]
4use minify_html::{minify, Cfg};
5use minijinja::Environment;
6use serde::Serialize;
7use std::{
8    borrow::Cow,
9    ops::{Deref, DerefMut},
10};
11
12pub mod filters;
13
14pub use minijinja::Error;
15
16#[cfg(feature = "derive")]
17pub use derive_jinja_renderer::*;
18
19#[cfg(feature = "minify")]
20const CFG: Cfg = Cfg {
21    do_not_minify_doctype: true,
22    ensure_spec_compliant_unquoted_attribute_values: true,
23    keep_closing_tags: true,
24    keep_html_and_head_opening_tags: true,
25    keep_spaces_between_attributes: true,
26    keep_input_type_text_attr: true,
27    preserve_brace_template_syntax: true,
28    minify_css: false,
29    minify_js: false,
30    keep_comments: false,
31    keep_ssi_comments: false,
32    preserve_chevron_percent_template_syntax: false,
33    remove_bangs: false,
34    remove_processing_instructions: false,
35};
36
37pub trait RenderContext: Serialize {
38    /// The name of the template to render
39    const TEMPLATE_NAME: &'static str;
40    /// The MIME type (Content-Type) of the data that gets rendered by this Template
41    const MIME_TYPE: &'static str;
42    /// render the context data
43    fn render(&self, renderer: &Renderer) -> Result<String, Error>;
44}
45
46#[derive(Debug, Serialize)]
47#[serde(rename_all = "camelCase")]
48pub struct EventInfo {
49    pub name: &'static str,
50    pub receivers: &'static [&'static str],
51    pub target: Cow<'static, str>,
52    pub swap: &'static str,
53    pub id_field: &'static str,
54}
55
56pub trait RenderEvent {
57    /// the event name
58    const EVENT_NAME: &'static str;
59    /// render the event data for SSE with the format as `encoded_json\nencoded_html`
60    fn render_event_data(&self, renderer: &Renderer) -> Result<String, Error>;
61    // event id
62    fn event_info(&self) -> EventInfo;
63}
64
65pub struct OwnedTemplate {
66    pub name: Cow<'static, str>,
67    pub data: Cow<'static, str>,
68}
69
70#[derive(Debug)]
71pub struct Renderer(Environment<'static>);
72
73impl Deref for Renderer {
74    type Target = Environment<'static>;
75
76    fn deref(&self) -> &Self::Target {
77        &self.0
78    }
79}
80
81impl DerefMut for Renderer {
82    fn deref_mut(&mut self) -> &mut Self::Target {
83        &mut self.0
84    }
85}
86
87impl Default for Renderer {
88    fn default() -> Self {
89        Self::new(Environment::new())
90    }
91}
92
93impl OwnedTemplate {
94    pub fn new(name: impl Into<Cow<'static, str>>, data: impl Into<Cow<'static, str>>) -> Self {
95        Self {
96            name: name.into(),
97            data: data.into(),
98        }
99    }
100}
101
102impl Renderer {
103    pub fn new(env: Environment<'static>) -> Self {
104        Self(env)
105    }
106
107    pub fn add_templates(
108        &mut self,
109        templates: impl Iterator<Item = OwnedTemplate>,
110    ) -> Result<(), Error> {
111        for tpl in templates {
112            self.add_template_owned(tpl.name, tpl.data)?;
113        }
114        Ok(())
115    }
116
117    pub fn render_template<T: Serialize>(&self, name: &str, context: &T) -> Result<String, Error> {
118        let tpl = self.0.get_template(name)?;
119        let mime = if name.ends_with("html.j2") {
120            "text/html; charset=utf-8"
121        } else if name.ends_with("json.j2") {
122            "application/json; charset=utf-8"
123        } else {
124            "text/plain; charset=utf-8"
125        };
126        self.render_minified(tpl, mime, context)
127    }
128
129    fn render_minified(
130        &self,
131        tpl: minijinja::Template<'_, '_>,
132        #[allow(unused_variables)] mime: &str,
133        context: &impl Serialize,
134    ) -> Result<String, Error> {
135        #[cfg(feature = "minify")]
136        {
137            let ret = tpl.render(context)?;
138            if mime.starts_with("text/html") {
139                let minified = minify(ret.as_bytes(), &CFG);
140                Ok(unsafe { String::from_utf8_unchecked(minified) })
141            } else {
142                Ok(ret)
143            }
144        }
145        #[cfg(not(feature = "minify"))]
146        {
147            tpl.render(context)
148        }
149    }
150}