1use std::sync::{Arc, RwLock};
2
3#[derive(Clone)]
4pub struct Template {
5 template_dir: String,
6 tera: Arc<RwLock<tera::Tera>>,
7}
8
9impl Template {
10 #[must_use]
11 pub fn new(template_dir: &str) -> Self {
12 let mut tera = tera::Tera::default();
13
14 let path = format!("{template_dir}/**/*");
15 if let Err(err) = tera.load_from_glob(&path) {
16 panic!("Parsing error(s): {err}");
17 }
18
19 Self {
20 tera: Arc::new(RwLock::new(tera)),
21 template_dir: template_dir.to_string(),
22 }
23 }
24
25 pub fn render(&self, template: &str, context: &tera::Context) -> tera::TeraResult<String> {
26 let tera = self.tera.read().unwrap();
27
28 tera.render(template, context)
29 }
30
31 #[cfg(debug_assertions)]
32 pub fn watch(self) {
33 use notify::Watcher;
34
35 let mut watcher = notify::recommended_watcher(move |res| match res {
36 Ok(event) => {
37 log::info!("reloading templates: {event:?}");
38
39 let mut tera = self.tera.write().unwrap();
40
41 match tera.full_reload() {
42 Ok(_) => log::info!("templates reloaded"),
43 Err(e) => log::error!("failed to reload templates: {e}"),
44 }
45 }
46 Err(e) => log::warn!("watch error: {e:?}"),
47 })
48 .unwrap();
49
50 log::debug!("watching {} for changes", self.template_dir);
51
52 watcher
53 .watch(
54 &std::path::PathBuf::from(&self.template_dir),
55 notify::RecursiveMode::Recursive,
56 )
57 .unwrap();
58 }
59
60 pub fn register_function<Func, Res>(
61 &mut self,
62 name: impl Into<std::borrow::Cow<'static, str>>,
63 function: Func,
64 ) where
65 Func: Fn(tera::Kwargs, &tera::State<'_>) -> Res + Sync + Send + 'static,
66 Res: tera::FunctionResult,
67 {
68 let mut tera = self.tera.write().unwrap();
69
70 tera.register_function(name, function);
71 }
72
73 pub fn register_filter<Func, Arg, Res>(
74 &mut self,
75 name: impl Into<std::borrow::Cow<'static, str>>,
76 filter: Func,
77 ) where
78 Func: tera::Filter<Arg, Res>
79 + for<'a> tera::Filter<<Arg as tera::ArgFromValue<'a>>::Output, Res>,
80 Arg: for<'a> tera::ArgFromValue<'a>,
81 Res: tera::FunctionResult,
82 {
83 let mut tera = self.tera.write().unwrap();
84
85 tera.register_filter(name, filter);
86 }
87
88 pub fn register_test<Func, Arg, Res>(
89 &mut self,
90 name: impl Into<std::borrow::Cow<'static, str>>,
91 test: Func,
92 ) where
93 Func:
94 tera::Test<Arg, Res> + for<'a> tera::Test<<Arg as tera::ArgFromValue<'a>>::Output, Res>,
95 Arg: for<'a> tera::ArgFromValue<'a>,
96 Res: tera::TestResult,
97 {
98 let mut tera = self.tera.write().unwrap();
99
100 tera.register_test(name, test);
101 }
102
103 #[cfg(not(debug_assertions))]
104 pub fn watch(self) {}
105}