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 const TEMPLATE_NAME: &'static str;
40 const MIME_TYPE: &'static str;
42 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 const EVENT_NAME: &'static str;
59 fn render_event_data(&self, renderer: &Renderer) -> Result<String, Error>;
61 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}