rocket_include_handlebars/debug/
reloadable.rs

1use std::{
2    collections::HashMap,
3    ops::{Deref, DerefMut},
4    path::PathBuf,
5    time::SystemTime,
6};
7
8use handlebars::{Handlebars, TemplateError};
9
10use crate::functions::add_helpers;
11
12#[derive(Debug)]
13/// Reloadable Handlebars.
14pub struct ReloadableHandlebars {
15    handlebars: Handlebars<'static>,
16    files:      HashMap<&'static str, (PathBuf, Option<SystemTime>)>,
17}
18
19impl ReloadableHandlebars {
20    /// Create an instance of `ReloadableHandlebars`.
21    #[inline]
22    pub fn new() -> ReloadableHandlebars {
23        let mut handlebars = Handlebars::new();
24
25        add_helpers(&mut handlebars);
26
27        ReloadableHandlebars {
28            handlebars,
29            files: HashMap::new(),
30        }
31    }
32
33    /// Register a template from a path and it can be reloaded automatically.
34    #[inline]
35    pub fn register_template_file<P: Into<PathBuf>>(
36        &mut self,
37        name: &'static str,
38        file_path: P,
39    ) -> Result<(), Box<TemplateError>> {
40        let file_path = file_path.into();
41
42        let metadata =
43            file_path.metadata().map_err(|err| TemplateError::from((err, String::from(name))))?;
44
45        let mtime = metadata.modified().ok();
46
47        self.handlebars.register_template_file(name, &file_path)?;
48
49        self.files.insert(name, (file_path, mtime));
50
51        Ok(())
52    }
53
54    /// Unregister a template from a file by a name.
55    #[inline]
56    pub fn unregister_template_file<S: AsRef<str>>(&mut self, name: S) -> Option<PathBuf> {
57        let name = name.as_ref();
58
59        match self.files.remove(name) {
60            Some((file_path, _)) => {
61                self.handlebars.unregister_template(name);
62
63                Some(file_path)
64            },
65            None => None,
66        }
67    }
68
69    /// Reload templates if needed.
70    #[inline]
71    pub fn reload_if_needed(&mut self) -> Result<(), Box<TemplateError>> {
72        for (&name, (file_path, mtime)) in &mut self.files {
73            let metadata = file_path
74                .metadata()
75                .map_err(|err| TemplateError::from((err, String::from(name))))?;
76
77            let (reload, new_mtime) = match mtime {
78                Some(mtime) => match metadata.modified() {
79                    Ok(new_mtime) => (new_mtime > *mtime, Some(new_mtime)),
80                    Err(_) => (true, None),
81                },
82                None => match metadata.modified() {
83                    Ok(new_mtime) => (true, Some(new_mtime)),
84                    Err(_) => (true, None),
85                },
86            };
87
88            if reload {
89                self.handlebars.register_template_file(name, &file_path)?;
90
91                *mtime = new_mtime;
92            }
93        }
94
95        Ok(())
96    }
97}
98
99impl Default for ReloadableHandlebars {
100    #[inline]
101    fn default() -> Self {
102        ReloadableHandlebars::new()
103    }
104}
105
106impl Deref for ReloadableHandlebars {
107    type Target = Handlebars<'static>;
108
109    #[inline]
110    fn deref(&self) -> &Self::Target {
111        &self.handlebars
112    }
113}
114
115impl DerefMut for ReloadableHandlebars {
116    #[inline]
117    fn deref_mut(&mut self) -> &mut Self::Target {
118        &mut self.handlebars
119    }
120}