rocket_dyn_templates/
fairing.rs

1use rocket::{Rocket, Build, Orbit};
2use rocket::fairing::{self, Fairing, Info, Kind};
3
4use crate::context::{Callback, Context, ContextManager};
5use crate::template::DEFAULT_TEMPLATE_DIR;
6use crate::engine::Engines;
7
8/// The TemplateFairing initializes the template system on attach, running
9/// custom_callback after templates have been loaded. In debug mode, the fairing
10/// checks for modifications to templates before every request and reloads them
11/// if necessary.
12pub struct TemplateFairing {
13    /// The user-provided customization callback, allowing the use of
14    /// functionality specific to individual template engines. In debug mode,
15    /// this callback might be run multiple times as templates are reloaded.
16    pub callback: Callback,
17}
18
19#[rocket::async_trait]
20impl Fairing for TemplateFairing {
21    fn info(&self) -> Info {
22        let kind = Kind::Ignite | Kind::Liftoff;
23        #[cfg(debug_assertions)] let kind = kind | Kind::Request;
24
25        Info { kind, name: "Templating" }
26    }
27
28    /// Initializes the template context. Templates will be searched for in the
29    /// `template_dir` config variable or the default ([DEFAULT_TEMPLATE_DIR]).
30    /// The user's callback, if any was supplied, is called to customize the
31    /// template engines. In debug mode, the `ContextManager::new` method
32    /// initializes a directory watcher for auto-reloading of templates.
33    async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
34        use rocket::figment::value::magic::RelativePathBuf;
35
36        let configured_dir = rocket.figment()
37            .extract_inner::<RelativePathBuf>("template_dir")
38            .map(|path| path.relative());
39
40        let path = match configured_dir {
41            Ok(dir) => dir,
42            Err(e) if e.missing() => DEFAULT_TEMPLATE_DIR.into(),
43            Err(e) => {
44                rocket::config::pretty_print_error(e);
45                return Err(rocket);
46            }
47        };
48
49        if let Some(ctxt) = Context::initialize(&path, &self.callback) {
50            Ok(rocket.manage(ContextManager::new(ctxt)))
51        } else {
52            error_!("Template initialization failed. Aborting launch.");
53            Err(rocket)
54        }
55    }
56
57    async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
58        use rocket::{figment::Source, log::PaintExt, yansi::Paint};
59
60        let cm = rocket.state::<ContextManager>()
61            .expect("Template ContextManager registered in on_ignite");
62
63        info!("{}{}:", "📐 ".emoji(), "Templating".magenta());
64        info_!("directory: {}", Source::from(&*cm.context().root).primary());
65        info_!("engines: {:?}", Engines::ENABLED_EXTENSIONS.primary());
66    }
67
68    #[cfg(debug_assertions)]
69    async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data<'_>) {
70        let cm = req.rocket().state::<ContextManager>()
71            .expect("Template ContextManager registered in on_ignite");
72
73        cm.reload_if_needed(&self.callback);
74    }
75
76}