Improve documentation for contrib library.

This commit is contained in:
Sergio Benitez 2016-09-29 21:26:06 -07:00
parent fec2866517
commit 804154e537
3 changed files with 131 additions and 51 deletions

View File

@ -10,7 +10,7 @@ use rocket::response::data;
use self::serde::{Serialize, Deserialize};
use self::serde_json::Error as JSONError;
/// The JSON datatype, which implements both `FromRequest` and `Responder`. This
/// The JSON type, which implements both `FromRequest` and `Responder`. This
/// type allows you to trivially consume and respond with JSON in your Rocket
/// application.
///

View File

@ -1,4 +1,5 @@
#[macro_export]
/// Returns a hashset with the extensions of all of the enabled template
/// engines from the set of template engined passed in.
macro_rules! engine_set {
($($feature:expr => $engine:ident),+,) => ({
use std::collections::HashSet;
@ -18,7 +19,11 @@ macro_rules! engine_set {
});
}
#[macro_export]
/// Renders the template named `name` with the given template info `info` and
/// context `ctxt` using one of the templates in the template set passed in. It
/// does this by checking if the template's extension matches the engine's
/// extension, and if so, calls the engine's `render` method. All of this only
/// happens for engine's that have been enabled as features by the user.
macro_rules! render_set {
($name:expr, $info:expr, $ctxt:expr, $($feature:expr => $engine:ident),+,) => ({$(
#[cfg(feature = $feature)]
@ -36,7 +41,7 @@ macro_rules! render_set {
fn $engine<T: Serialize>(_: &str, _: &TemplateInfo, _: &T)
-> Option<Template> { None }
if let Some(template) = $engine($name, &$info, &$ctxt) {
if let Some(template) = $engine($name, &$info, $ctxt) {
return template
}
)+});

View File

@ -18,6 +18,85 @@ use rocket::response::{Content, Outcome, FreshHyperResponse, Responder};
use rocket::{Rocket, ContentType};
use self::glob::glob;
/// The Template type implements generic support for template rendering in
/// Rocket.
///
/// Templating in Rocket words by first discovering all of the templates inside
/// the template directory. The template directory is configurable via the
/// `template_dir` configuration parameter. The path set in `template_dir`
/// should be relative to the Rocket configuration file. See the [configuration
/// chapter](https://rocket.rs/guide/configuration) of the guide for more
/// information on configuration.
///
/// Templates are discovered according to their extension. At present, this
/// library supports the following templates and extensions:
///
/// * **Tera**: `.tera`
/// * **Handlebars**: `.hbs`
///
/// Any file that ends with one of these extension will be discovered and
/// rendered with the corresponding templating engine. The name of the template
/// will be the path to the template file relative to `template_dir` minus at
/// most two extensions. The following are examples of template names (on the
/// right) given that the template is at the path on the left.
///
/// * `{template_dir}/index.html.hbs` => index
/// * `{template_dir}/index.hbs` => index
/// * `{template_dir}/dir/index.hbs` => dir/index
/// * `{template_dir}/dir/index.html.hbs` => dir/index
/// * `{template_dir}/index.template.html.hbs` => index.template
/// * `{template_dir}/subdir/index.template.html.hbs` => subdir/index.template
///
/// The recommended naming scheme is to use two extensions: one for the file
/// type, and one for the template extension. This means that template
/// extensions should look like: `.html.hbs`, `.html.tera`, `.xml.hbs`, etc.
///
/// 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
/// can be any type that implements `Serialize` from
/// [Serde](https://github.com/serde-rs/json) and would serialize to an `Object`
/// value.
///
/// # Examples
///
/// To render a template named "index" with a `HashMap` as the context:
///
/// ```rust
/// use rocket_contrib::Template;
/// use std::collections::HashMap;
///
/// let context: HashMap<&str, &str> = HashMap::new();
/// // ... add key/value pairs to `context` ...
/// let _template = Template::render("index", &context);
/// ```
///
/// The Template type implements Rocket's `Responder` trait, so it can be
/// returned from a request handler directly:
///
/// ```rust,ignore
/// #[get("/")]
/// fn index() -> Template {
/// let context = ...;
/// Template::render("index", &context)
/// }
/// ```
#[derive(Debug)]
pub struct Template(Option<String>, Option<String>);
#[derive(Debug)]
pub struct TemplateInfo {
/// The complete path, including `template_dir`, to this template.
full_path: PathBuf,
/// The complete path, without `template_dir`, to this template.
path: PathBuf,
/// The complete path, with `template_dir`, without the template extension.
canonical_path: PathBuf,
/// The extension of for the engine of this template.
extension: String,
/// The extension before the engine extension in the template, if any.
data_type: Option<String>
}
lazy_static! {
static ref TEMPLATES: HashMap<String, TemplateInfo> = discover_templates();
// FIXME: Ensure template_dir is an absolute path starting at crate root.
@ -25,55 +104,14 @@ lazy_static! {
Rocket::config("template_dir").unwrap_or("templates").to_string();
}
/// Removes the file path's extension or does nothing if there is none.
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
PathBuf::from(path.as_ref().file_stem().unwrap())
}
fn discover_templates() -> HashMap<String, TemplateInfo> {
// Keep this set in-sync with the `render_set` invocation.
let engines = engine_set![
"tera_templates" => tera_templates,
"handlebars_templates" => handlebars_templates,
];
let mut templates = HashMap::new();
for ext in engines {
let mut path: PathBuf = [&*TEMPLATE_DIR, "**", "*"].iter().collect();
path.set_extension(ext);
for p in glob(path.to_str().unwrap()).unwrap().filter_map(Result::ok) {
let canonical_path = remove_extension(&p);
let name = remove_extension(&canonical_path);
let data_type = canonical_path.extension();
templates.insert(name.to_string_lossy().into_owned(), TemplateInfo {
full_path: p.to_path_buf(),
path: p.strip_prefix(&*TEMPLATE_DIR).unwrap().to_path_buf(),
canonical_path: canonical_path.clone(),
extension: p.extension().unwrap().to_string_lossy().into_owned(),
data_type: data_type.map(|d| d.to_string_lossy().into_owned())
});
}
}
templates
}
#[derive(Debug)]
pub struct Template(Option<String>, Option<String>);
#[derive(Debug)]
pub struct TemplateInfo {
full_path: PathBuf,
path: PathBuf,
canonical_path: PathBuf,
extension: String,
data_type: Option<String>
}
impl Template {
pub fn render<S, T>(name: S, context: T) -> Template
/// Render the template named `name` with the context `context`. The
/// template is not actually rendered until the response is needed by
/// Rocket. As such, the `Template` type should be used only as a
/// `Responder`.
pub fn render<S, T>(name: S, context: &T) -> Template
where S: AsRef<str>, T: Serialize
{
{
let name = name.as_ref();
let template = TEMPLATES.get(name);
if template.is_none() {
@ -104,3 +142,40 @@ impl Responder for Template {
}
}
}
/// Removes the file path's extension or does nothing if there is none.
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
PathBuf::from(path.as_ref().file_stem().unwrap())
}
/// Returns a HashMap of `TemplateInfo`'s for all of the templates in
/// `TEMPLATE_DIR`. Templates are all files that match one of the extensions for
/// engine's in `engine_set`.
fn discover_templates() -> HashMap<String, TemplateInfo> {
// Keep this set in-sync with the `render_set` invocation.
let engines = engine_set![
"tera_templates" => tera_templates,
"handlebars_templates" => handlebars_templates,
];
let mut templates = HashMap::new();
for ext in engines {
let mut path: PathBuf = [&*TEMPLATE_DIR, "**", "*"].iter().collect();
path.set_extension(ext);
for p in glob(path.to_str().unwrap()).unwrap().filter_map(Result::ok) {
let canonical_path = remove_extension(&p);
let name = remove_extension(&canonical_path);
let data_type = canonical_path.extension();
templates.insert(name.to_string_lossy().into_owned(), TemplateInfo {
full_path: p.to_path_buf(),
path: p.strip_prefix(&*TEMPLATE_DIR).unwrap().to_path_buf(),
canonical_path: canonical_path.clone(),
extension: p.extension().unwrap().to_string_lossy().into_owned(),
data_type: data_type.map(|d| d.to_string_lossy().into_owned())
});
}
}
templates
}