mirror of https://github.com/rwf2/Rocket.git
parent
007c4b093f
commit
2f98299272
|
@ -1,3 +1,5 @@
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use crate::templates::{DEFAULT_TEMPLATE_DIR, Context, Engines};
|
use crate::templates::{DEFAULT_TEMPLATE_DIR, Context, Engines};
|
||||||
|
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
|
@ -5,6 +7,8 @@ use rocket::fairing::{Fairing, Info, Kind};
|
||||||
|
|
||||||
pub(crate) use self::context::ContextManager;
|
pub(crate) use self::context::ContextManager;
|
||||||
|
|
||||||
|
type Callback = Box<dyn Fn(&mut Engines) -> Result<(), Box<dyn Error>>+ Send + Sync + 'static>;
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
mod context {
|
mod context {
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -37,7 +41,7 @@ mod context {
|
||||||
|
|
||||||
use notify::{raw_watcher, RawEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{raw_watcher, RawEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
|
||||||
use crate::templates::{Context, Engines};
|
use super::{Callback, Context};
|
||||||
|
|
||||||
/// Wraps a Context. With `cfg(debug_assertions)` active, this structure
|
/// Wraps a Context. With `cfg(debug_assertions)` active, this structure
|
||||||
/// additionally provides a method to reload the context at runtime.
|
/// additionally provides a method to reload the context at runtime.
|
||||||
|
@ -88,7 +92,7 @@ mod context {
|
||||||
/// have been changes since the last reload, all templates are
|
/// have been changes since the last reload, all templates are
|
||||||
/// reinitialized from disk and the user's customization callback is run
|
/// reinitialized from disk and the user's customization callback is run
|
||||||
/// again.
|
/// again.
|
||||||
pub fn reload_if_needed<F: Fn(&mut Engines)>(&self, custom_callback: F) {
|
pub fn reload_if_needed(&self, callback: &Callback) {
|
||||||
self.watcher.as_ref().map(|w| {
|
self.watcher.as_ref().map(|w| {
|
||||||
let rx_lock = w.lock().expect("receive queue lock");
|
let rx_lock = w.lock().expect("receive queue lock");
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
|
@ -100,11 +104,17 @@ mod context {
|
||||||
info_!("Change detected: reloading templates.");
|
info_!("Change detected: reloading templates.");
|
||||||
let mut ctxt = self.context_mut();
|
let mut ctxt = self.context_mut();
|
||||||
if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) {
|
if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) {
|
||||||
custom_callback(&mut new_ctxt.engines);
|
match callback(&mut new_ctxt.engines) {
|
||||||
*ctxt = new_ctxt;
|
Ok(()) => *ctxt = new_ctxt,
|
||||||
|
Err(e) => {
|
||||||
|
warn_!("The template customization callback returned an error:");
|
||||||
|
warn_!("{}", e);
|
||||||
|
warn_!("The existing templates will remain active.");
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
warn_!("An error occurred while reloading templates.");
|
warn_!("An error occurred while reloading templates.");
|
||||||
warn_!("The previous templates will remain active.");
|
warn_!("The existing templates will remain active.");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -120,7 +130,7 @@ pub struct TemplateFairing {
|
||||||
/// The user-provided customization callback, allowing the use of
|
/// The user-provided customization callback, allowing the use of
|
||||||
/// functionality specific to individual template engines. In debug mode,
|
/// functionality specific to individual template engines. In debug mode,
|
||||||
/// this callback might be run multiple times as templates are reloaded.
|
/// this callback might be run multiple times as templates are reloaded.
|
||||||
pub custom_callback: Box<dyn Fn(&mut Engines) + Send + Sync + 'static>,
|
pub callback: Callback,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
|
@ -161,12 +171,20 @@ impl Fairing for TemplateFairing {
|
||||||
use crate::templates::Engines;
|
use crate::templates::Engines;
|
||||||
|
|
||||||
info!("{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:"));
|
info!("{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:"));
|
||||||
|
|
||||||
|
match (self.callback)(&mut ctxt.engines) {
|
||||||
|
Ok(()) => {
|
||||||
info_!("directory: {}", Paint::white(root));
|
info_!("directory: {}", Paint::white(root));
|
||||||
info_!("engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS));
|
info_!("engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS));
|
||||||
|
|
||||||
(self.custom_callback)(&mut ctxt.engines);
|
|
||||||
Ok(rocket.manage(ContextManager::new(ctxt)))
|
Ok(rocket.manage(ContextManager::new(ctxt)))
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error_!("The template customization callback returned an error:");
|
||||||
|
error_!("{}", e);
|
||||||
|
Err(rocket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
None => Err(rocket),
|
None => Err(rocket),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,6 +194,6 @@ impl Fairing for TemplateFairing {
|
||||||
let cm = req.managed_state::<ContextManager>()
|
let cm = req.managed_state::<ContextManager>()
|
||||||
.expect("Template ContextManager registered in on_attach");
|
.expect("Template ContextManager registered in on_attach");
|
||||||
|
|
||||||
cm.reload_if_needed(&*self.custom_callback);
|
cm.reload_if_needed(&self.callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,11 @@
|
||||||
//! ## Template Fairing
|
//! ## Template Fairing
|
||||||
//!
|
//!
|
||||||
//! Template discovery is actualized by the template fairing, which itself is
|
//! Template discovery is actualized by the template fairing, which itself is
|
||||||
//! created via [`Template::fairing()`] or [`Template::custom()`], the latter of
|
//! created via [`Template::fairing()`], [`Template::custom()`], or
|
||||||
//! which allows for customizations to the templating engine. In order for _any_
|
//! [`Template::try_custom()`], the latter two allowing customizations to
|
||||||
//! templates to be rendered, the template fairing _must_ be
|
//! enabled templating engines. In order for _any_ templates to be rendered, the
|
||||||
//! [attached](rocket::Rocket::attach()) to the running Rocket instance. Failure
|
//! template fairing _must_ be [attached](rocket::Rocket::attach()) to the
|
||||||
//! to do so will result in a run-time error.
|
//! running Rocket instance. Failure to do so will result in a run-time error.
|
||||||
//!
|
//!
|
||||||
//! Templates are rendered with the `render` method. The method takes in the
|
//! Templates are rendered with the `render` method. The method takes in the
|
||||||
//! name of a template and a context to render the template with. The context
|
//! name of a template and a context to render the template with. The context
|
||||||
|
@ -110,10 +110,6 @@
|
||||||
//! template reloading is disabled to improve performance and cannot be enabled.
|
//! template reloading is disabled to improve performance and cannot be enabled.
|
||||||
//!
|
//!
|
||||||
//! [`Serialize`]: serde::Serialize
|
//! [`Serialize`]: serde::Serialize
|
||||||
//! [`Template`]: crate::templates::Template
|
|
||||||
//! [`Template::fairing()`]: crate::templates::Template::fairing()
|
|
||||||
//! [`Template::custom()`]: crate::templates::Template::custom()
|
|
||||||
//! [`Template::render()`]: crate::templates::Template::render()
|
|
||||||
|
|
||||||
#[cfg(feature = "tera_templates")] pub extern crate tera;
|
#[cfg(feature = "tera_templates")] pub extern crate tera;
|
||||||
#[cfg(feature = "tera_templates")] mod tera_templates;
|
#[cfg(feature = "tera_templates")] mod tera_templates;
|
||||||
|
@ -139,6 +135,7 @@ use serde_json::{Value, to_value};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
use rocket::request::Request;
|
use rocket::request::Request;
|
||||||
|
@ -254,9 +251,12 @@ impl Template {
|
||||||
/// Returns a fairing that initializes and maintains templating state.
|
/// Returns a fairing that initializes and maintains templating state.
|
||||||
///
|
///
|
||||||
/// Unlike [`Template::fairing()`], this method allows you to configure
|
/// Unlike [`Template::fairing()`], this method allows you to configure
|
||||||
/// templating engines via the parameter `f`. Note that only the enabled
|
/// templating engines via the function `f`. Note that only the enabled
|
||||||
/// templating engines will be accessible from the `Engines` type.
|
/// templating engines will be accessible from the `Engines` type.
|
||||||
///
|
///
|
||||||
|
/// This method does not allow the function `f` to fail. If `f` is fallible,
|
||||||
|
/// use [`Template::try_custom()`] instead.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -275,10 +275,42 @@ impl Template {
|
||||||
/// # ;
|
/// # ;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn custom<F>(f: F) -> impl Fairing
|
pub fn custom<F: Send + Sync + 'static>(f: F) -> impl Fairing
|
||||||
where F: Fn(&mut Engines) + Send + Sync + 'static
|
where F: Fn(&mut Engines)
|
||||||
{
|
{
|
||||||
TemplateFairing { custom_callback: Box::new(f) }
|
Self::try_custom(move |engines| { f(engines); Ok(()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a fairing that initializes and maintains templating state.
|
||||||
|
///
|
||||||
|
/// This variant of [`Template::custom()`] allows a fallible `f`. If `f`
|
||||||
|
/// returns an error during initialization, it will cancel the launch. If
|
||||||
|
/// `f` returns an error during template reloading (in debug mode), then the
|
||||||
|
/// newly-reloaded templates are discarded.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// extern crate rocket;
|
||||||
|
/// extern crate rocket_contrib;
|
||||||
|
///
|
||||||
|
/// use rocket_contrib::templates::Template;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// rocket::ignite()
|
||||||
|
/// // ...
|
||||||
|
/// .attach(Template::try_custom(|engines| {
|
||||||
|
/// // engines.handlebars.register_helper ...
|
||||||
|
/// Ok(())
|
||||||
|
/// }))
|
||||||
|
/// // ...
|
||||||
|
/// # ;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn try_custom<F: Send + Sync + 'static>(f: F) -> impl Fairing
|
||||||
|
where F: Fn(&mut Engines) -> Result<(), Box<dyn Error>>
|
||||||
|
{
|
||||||
|
TemplateFairing { callback: Box::new(f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the template named `name` with the context `context`. The
|
/// Render the template named `name` with the context `context`. The
|
||||||
|
|
|
@ -32,6 +32,23 @@ mod templates_tests {
|
||||||
.mount("/", routes![template_check, is_reloading])
|
.mount("/", routes![template_check, is_reloading])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_callback_error() {
|
||||||
|
use rocket::{local::blocking::Client, error::ErrorKind::FailedFairings};
|
||||||
|
|
||||||
|
let rocket = rocket::ignite().attach(Template::try_custom(|_| {
|
||||||
|
Err("error reloading templates!".into())
|
||||||
|
}));
|
||||||
|
|
||||||
|
match Client::untracked(rocket) {
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
FailedFairings(failures) => assert_eq!(failures[0], "Templates"),
|
||||||
|
_ => panic!("Wrong kind of launch error"),
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong kind of error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tera_templates")]
|
#[cfg(feature = "tera_templates")]
|
||||||
mod tera_tests {
|
mod tera_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in New Issue