Skip to main content

rocket_include_tera/debug/
reloadable.rs

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