From 5a4e66ec439411d30f16e5c045f8e4986f5883a4 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Mon, 24 May 2021 18:58:05 -0700 Subject: [PATCH] Split 'rocket_contrib' into distinct crates. This follows the completed graduation of stable contrib features into core, removing 'rocket_contrib' in its entirety in favor of two new crates. These crates are versioned independently of Rocket's core libraries, allowing upgrades to dependencies without consideration for versions in core libraries. 'rocket_dyn_templates' replaces the contrib 'templates' features. While largely a 1-to-1 copy, it makes the following changes: * the 'tera_templates' feature is now 'tera' * the 'handlebars_templates' feature is now 'handlebars' * fails to compile if neither 'tera' nor 'handlebars' is enabled 'rocket_sync_db_pools' replaces the contrib 'database' features. It makes no changes to the replaced features except that the `database` attribute is properly documented at the crate root. --- Cargo.toml | 5 +- README.md | 35 +-- contrib/codegen/Cargo.toml | 28 -- contrib/codegen/src/lib.rs | 46 --- contrib/dyn_templates/Cargo.toml | 40 +++ contrib/dyn_templates/README.md | 52 ++++ .../src}/context.rs | 4 +- .../templates => dyn_templates/src}/engine.rs | 52 ++-- .../src}/fairing.rs | 4 +- .../src}/handlebars_templates.rs | 4 +- .../mod.rs => dyn_templates/src/lib.rs} | 182 +++++------ .../src}/metadata.rs | 14 +- .../src}/tera_templates.rs | 5 +- contrib/dyn_templates/tests/templates.rs | 238 ++++++++++++++ .../templates/hbs/common/footer.html.hbs | 0 .../templates/hbs/common/header.html.hbs | 0 .../tests/templates/hbs/reload.txt.hbs | 0 .../tests/templates/hbs/test.html.hbs | 0 .../tests/templates/tera/base.txt.tera | 0 .../tests/templates/tera/html_test.html.tera | 0 .../tests/templates/tera/txt_test.txt.tera | 0 contrib/lib/Cargo.toml | 74 ----- contrib/lib/src/compression/fairing.rs | 154 --------- contrib/lib/src/compression/mod.rs | 139 --------- contrib/lib/src/compression/responder.rs | 47 --- contrib/lib/src/lib.rs | 46 --- .../lib/tests/compress_responder.rs.disabled | 238 -------------- .../lib/tests/compression_fairing.rs.disabled | 293 ------------------ contrib/lib/tests/templates.rs | 242 --------------- contrib/sync_db_pools/README.md | 58 ++++ contrib/sync_db_pools/codegen/Cargo.toml | 22 ++ .../codegen/src/database.rs | 28 +- contrib/sync_db_pools/codegen/src/lib.rs | 50 +++ .../tests/ui-fail-nightly/database-syntax.rs | 0 .../ui-fail-nightly/database-syntax.stderr | 0 .../tests/ui-fail-nightly/database-types.rs | 0 .../ui-fail-nightly/database-types.stderr | 16 +- .../tests/ui-fail-stable/database-syntax.rs | 0 .../ui-fail-stable/database-syntax.stderr | 0 .../tests/ui-fail-stable/database-types.rs | 0 .../ui-fail-stable/database-types.stderr | 16 +- .../codegen/tests/ui-fail.rs | 0 .../codegen/tests/ui-fail/database-syntax.rs | 4 +- .../codegen/tests/ui-fail/database-types.rs | 3 +- contrib/sync_db_pools/lib/Cargo.toml | 44 +++ .../lib/src}/config.rs | 6 +- .../lib/src}/connection.rs | 3 +- .../lib/src}/error.rs | 2 +- .../mod.rs => sync_db_pools/lib/src/lib.rs} | 96 +++--- .../lib/src}/poolable.rs | 9 +- .../lib/tests/databases.rs | 7 +- core/lib/src/data/limits.rs | 5 +- core/lib/src/response/content.rs | 2 +- core/lib/src/response/mod.rs | 9 - examples/cookies/Cargo.toml | 7 +- examples/cookies/src/main.rs | 2 +- examples/cookies/src/message.rs | 2 +- examples/cookies/src/session.rs | 2 +- examples/databases/Cargo.toml | 5 +- examples/databases/src/diesel_sqlite.rs | 2 +- examples/databases/src/main.rs | 2 +- examples/databases/src/rusqlite.rs | 2 +- examples/databases/src/sqlx.rs | 2 +- examples/forms/Cargo.toml | 5 +- examples/forms/src/main.rs | 2 +- examples/templating/Cargo.toml | 7 +- examples/templating/src/hbs.rs | 2 +- examples/templating/src/main.rs | 2 +- examples/templating/src/tera.rs | 2 +- examples/templating/src/tests.rs | 2 +- examples/todo/Cargo.toml | 11 +- examples/todo/src/main.rs | 4 +- scripts/config.sh | 12 +- scripts/mk-docs.sh | 6 +- scripts/test.sh | 28 +- site/guide/4-requests.md | 1 - site/guide/5-responses.md | 22 +- site/guide/6-state.md | 36 +-- site/guide/9-configuration.md | 5 +- site/overview.toml | 4 +- site/tests/Cargo.toml | 9 +- 81 files changed, 837 insertions(+), 1671 deletions(-) delete mode 100644 contrib/codegen/Cargo.toml delete mode 100644 contrib/codegen/src/lib.rs create mode 100644 contrib/dyn_templates/Cargo.toml create mode 100644 contrib/dyn_templates/README.md rename contrib/{lib/src/templates => dyn_templates/src}/context.rs (99%) rename contrib/{lib/src/templates => dyn_templates/src}/engine.rs (70%) rename contrib/{lib/src/templates => dyn_templates/src}/fairing.rs (95%) rename contrib/{lib/src/templates => dyn_templates/src}/handlebars_templates.rs (93%) rename contrib/{lib/src/templates/mod.rs => dyn_templates/src/lib.rs} (77%) rename contrib/{lib/src/templates => dyn_templates/src}/metadata.rs (91%) rename contrib/{lib/src/templates => dyn_templates/src}/tera_templates.rs (96%) create mode 100644 contrib/dyn_templates/tests/templates.rs rename contrib/{lib => dyn_templates}/tests/templates/hbs/common/footer.html.hbs (100%) rename contrib/{lib => dyn_templates}/tests/templates/hbs/common/header.html.hbs (100%) rename contrib/{lib => dyn_templates}/tests/templates/hbs/reload.txt.hbs (100%) rename contrib/{lib => dyn_templates}/tests/templates/hbs/test.html.hbs (100%) rename contrib/{lib => dyn_templates}/tests/templates/tera/base.txt.tera (100%) rename contrib/{lib => dyn_templates}/tests/templates/tera/html_test.html.tera (100%) rename contrib/{lib => dyn_templates}/tests/templates/tera/txt_test.txt.tera (100%) delete mode 100644 contrib/lib/Cargo.toml delete mode 100644 contrib/lib/src/compression/fairing.rs delete mode 100644 contrib/lib/src/compression/mod.rs delete mode 100644 contrib/lib/src/compression/responder.rs delete mode 100644 contrib/lib/src/lib.rs delete mode 100644 contrib/lib/tests/compress_responder.rs.disabled delete mode 100644 contrib/lib/tests/compression_fairing.rs.disabled delete mode 100644 contrib/lib/tests/templates.rs create mode 100644 contrib/sync_db_pools/README.md create mode 100644 contrib/sync_db_pools/codegen/Cargo.toml rename contrib/{ => sync_db_pools}/codegen/src/database.rs (82%) create mode 100644 contrib/sync_db_pools/codegen/src/lib.rs rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-nightly/database-syntax.rs (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-nightly/database-syntax.stderr (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-nightly/database-types.rs (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-nightly/database-types.stderr (62%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-stable/database-syntax.rs (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-stable/database-syntax.stderr (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-stable/database-types.rs (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail-stable/database-types.stderr (62%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail.rs (100%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail/database-syntax.rs (85%) rename contrib/{ => sync_db_pools}/codegen/tests/ui-fail/database-types.rs (64%) create mode 100644 contrib/sync_db_pools/lib/Cargo.toml rename contrib/{lib/src/databases => sync_db_pools/lib/src}/config.rs (96%) rename contrib/{lib/src/databases => sync_db_pools/lib/src}/connection.rs (98%) rename contrib/{lib/src/databases => sync_db_pools/lib/src}/error.rs (94%) rename contrib/{lib/src/databases/mod.rs => sync_db_pools/lib/src/lib.rs} (83%) rename contrib/{lib/src/databases => sync_db_pools/lib/src}/poolable.rs (98%) rename contrib/{ => sync_db_pools}/lib/tests/databases.rs (94%) diff --git a/Cargo.toml b/Cargo.toml index 54775e12..52fc6a68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ members = [ "core/lib/", "core/codegen/", "core/http/", - "contrib/lib", - "contrib/codegen", + "contrib/sync_db_pools/codegen/", + "contrib/sync_db_pools/lib/", + "contrib/dyn_templates/", "site/tests", ] diff --git a/README.md b/README.md index 3243d46f..2ec2da90 100644 --- a/README.md +++ b/README.md @@ -69,29 +69,25 @@ You should see `Hello, world!` by visiting `http://localhost:8000`. ## Building and Testing -### Core and Contrib - The `core` directory contains the three core libraries: `lib`, `codegen`, and -`http`. The `contrib` directory contains officially supported community -contributions and similarly consists of `lib` and `codegen`. +`http` published as `rocket`, `rocket_codegen` and `rocket_http`, respectively. +The latter two are implementations details and are reexported from `rocket`. -Public APIs are exposed via `lib` packages: `core/lib` is distributed as the -`rocket` crate while `contrib/lib` is distributed as the `rocket_contrib` crate. -The remaining crates are implementation details. - -### Library Testing +### Testing Rocket's complete test suite can be run with `./scripts/test.sh` from the root -of the source tree. The script builds and tests all libraries and examples. It -accepts the following flags: +of the source tree. The script builds and tests all libraries and examples in +all configurations. It accepts the following flags: - * `--contrib`: tests each `contrib` feature individually - * `--core`: tests each `core` feature individually - * `--release`: runs the testing suite in `release` mode + * `--examples`: tests all examples in `examples/` + * `--contrib`: tests each `contrib` library and feature individually + * `--core`: tests each `core/lib` feature individually + * `--benchmarks`: runs all benchmarks + * `--all`: runs all tests in all configurations Additionally, a `+${toolchain}` flag, where `${toolchain}` is a valid `rustup` toolchain string, can be passed as the first parameter. The flag is forwarded to -`cargo` commands. +`cargo` commands. Any other extra parameters are passed directly to `cargo`. To test crates individually, simply run `cargo test --all-features` in the crate's directory. @@ -99,10 +95,11 @@ crate's directory. ### Codegen Testing Code generation diagnostics are tested using [`trybuild`]; tests can be found in -the `codegen/tests/ui-fail` directory of both `core` and `contrib`. Each test is -symlinked into sibling `ui-fail-stable` and `ui-fail-nightly` directories which -contain the expected error output for stable and nightly compilers, -respectively. +the `codegen/tests/ui-fail` directories of respective `codegen` crates. Each +test is symlinked into sibling `ui-fail-stable` and `ui-fail-nightly` +directories which contain the expected error output for stable and nightly +compilers, respectively. To update codegen test UI output, run a codegen test +suite with `TRYBUILD=overwrite` and inspect the `diff` of `.std*` files. [`trybuild`]: https://docs.rs/trybuild/1 diff --git a/contrib/codegen/Cargo.toml b/contrib/codegen/Cargo.toml deleted file mode 100644 index 100420e1..00000000 --- a/contrib/codegen/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "rocket_contrib_codegen" -version = "0.5.0-dev" -authors = ["Sergio Benitez "] -description = "Procedural macros for the Rocket contrib libraries." -documentation = "https://api.rocket.rs/master/rocket_contrib/" -homepage = "https://rocket.rs" -repository = "https://github.com/SergioBenitez/Rocket" -readme = "../../README.md" -keywords = ["rocket", "contrib", "code", "generation", "proc-macro"] -license = "MIT OR Apache-2.0" -edition = "2018" - -[features] -database_attribute = [] - -[lib] -proc-macro = true - -[dependencies] -quote = "1.0" -devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "df00b5" } - -[dev-dependencies] -rocket = { version = "0.5.0-dev", path = "../../core/lib" } -rocket_contrib = { version = "0.5.0-dev", path = "../lib", features = ["diesel_sqlite_pool"] } -trybuild = "1.0" -version_check = "0.9" diff --git a/contrib/codegen/src/lib.rs b/contrib/codegen/src/lib.rs deleted file mode 100644 index 9e8c2e8e..00000000 --- a/contrib/codegen/src/lib.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![recursion_limit="256"] - -#![warn(rust_2018_idioms)] - -//! # Rocket Contrib - Code Generation -//! -//! This crate implements the code generation portion of the Rocket Contrib -//! crate. This is for officially sanctioned contributor libraries that require -//! code generation of some kind. -//! -//! This crate includes custom derives and procedural macros and will expand -//! as-needed if future `rocket_contrib` features require code generation -//! facilities. -//! -//! ## Procedural Macros -//! -//! This crate implements the following procedural macros: -//! -//! * **databases** -//! -//! The syntax for the `databases` macro is: -//! -//!
-//! macro := database(DATABASE_NAME)
-//! DATABASE_NAME := (string literal)
-//! 
- -#[allow(unused_imports)] -#[macro_use] extern crate quote; - -#[allow(unused_imports)] -use devise::{syn, proc_macro2}; - -#[cfg(feature = "database_attribute")] -mod database; - -#[allow(unused_imports)] -use proc_macro::TokenStream; - -/// The procedural macro for the `databases` annotation. -#[cfg(feature = "database_attribute")] -#[proc_macro_attribute] -pub fn database(attr: TokenStream, input: TokenStream) -> TokenStream { - crate::database::database_attr(attr, input) - .unwrap_or_else(|diag| diag.emit_as_item_tokens().into()) -} diff --git a/contrib/dyn_templates/Cargo.toml b/contrib/dyn_templates/Cargo.toml new file mode 100644 index 00000000..26555b7d --- /dev/null +++ b/contrib/dyn_templates/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "rocket_dyn_templates" +version = "0.1.0-dev" +authors = ["Sergio Benitez "] +description = "Dynamic templating engine integration for Rocket." +documentation = "https://api.rocket.rs/master/rocket_dyn_templates/" +homepage = "https://rocket.rs" +repository = "https://github.com/SergioBenitez/Rocket/contrib/dyn_templates" +readme = "README.md" +keywords = ["rocket", "framework", "templates", "templating", "engine"] +license = "MIT OR Apache-2.0" +edition = "2018" + +[features] +tera = ["_tera"] +handlebars = ["_handlebars"] + +[dependencies] +serde = "1.0" +serde_json = "1.0.26" +glob = "0.3" +notify = "4.0.6" +normpath = "0.2" + +[dependencies.rocket] +path = "../../core/lib" +default-features = false + +[dependencies._tera] +package = "tera" +version = "1.10.0" +optional = true + +[dependencies._handlebars] +package = "handlebars" +version = "3.0" +optional = true + +[package.metadata.docs.rs] +all-features = true diff --git a/contrib/dyn_templates/README.md b/contrib/dyn_templates/README.md new file mode 100644 index 00000000..6a04bc88 --- /dev/null +++ b/contrib/dyn_templates/README.md @@ -0,0 +1,52 @@ +# `dyn_templates` [![ci.svg]][ci] [![crates.io]][crate] [![docs.svg]][crate docs] + +[crates.io]: https://img.shields.io/crates/v/rocket_dyn_templates.svg +[crate]: https://crates.io/crates/rocket_dyn_templates +[docs.svg]: https://img.shields.io/badge/web-master-red.svg?style=flat&label=docs&colorB=d33847 +[crate docs]: https://api.rocket.rs/master/rocket_dyn_templates +[ci.svg]: https://github.com/SergioBenitez/Rocket/workflows/CI/badge.svg +[ci]: https://github.com/SergioBenitez/Rocket/actions + +This crate adds support for dynamic template rendering to Rocket. It +automatically discovers templates, provides a `Responder` to render templates, +and automatically reloads templates when compiled in debug mode. At present, it +supports [Handlebars] and [Tera]. + +[Tera]: https://docs.rs/crate/tera/1 +[Handlebars]: https://docs.rs/crate/handlebars/3 + +# Usage + + 1. Enable the `rocket_dyn_templates` feature corresponding to your templating + engine(s) of choice: + + ```toml + [dependencies.rocket_dyn_templates] + version = "0.1.0-dev" + default-features = false + features = ["handlebars", "tera"] + ``` + + 1. Write your template files in Handlebars (`.hbs`) and/or Tera (`.tera`) in + the configurable `template_dir` directory (default: + `{rocket_root}/templates`). + + 2. Attach `Template::fairing()` and return a `Template` using + `Template::render()`, supplying the name of the template file **minus the + last two extensions**: + + ```rust + use rocket_dyn_templates::Template; + + #[launch] + fn rocket() -> _ { + rocket::build().attach(Template::fairing()) + } + + #[get("/")] + fn index() -> Template { + Template::render("template-name", &context) + } + ``` + +See the [crate docs] for full details. diff --git a/contrib/lib/src/templates/context.rs b/contrib/dyn_templates/src/context.rs similarity index 99% rename from contrib/lib/src/templates/context.rs rename to contrib/dyn_templates/src/context.rs index 6b9b9fdc..aa53ac9a 100644 --- a/contrib/lib/src/templates/context.rs +++ b/contrib/dyn_templates/src/context.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use std::collections::HashMap; use std::error::Error; -use crate::templates::{Engines, TemplateInfo}; +use crate::{Engines, TemplateInfo}; use rocket::http::ContentType; use normpath::PathExt; @@ -88,7 +88,7 @@ impl Context { #[cfg(not(debug_assertions))] mod manager { use std::ops::Deref; - use crate::templates::Context; + use crate::Context; /// Wraps a Context. With `cfg(debug_assertions)` active, this structure /// additionally provides a method to reload the context at runtime. diff --git a/contrib/lib/src/templates/engine.rs b/contrib/dyn_templates/src/engine.rs similarity index 70% rename from contrib/lib/src/templates/engine.rs rename to contrib/dyn_templates/src/engine.rs index 26029c86..d4bc0d77 100644 --- a/contrib/lib/src/templates/engine.rs +++ b/contrib/dyn_templates/src/engine.rs @@ -3,10 +3,10 @@ use std::collections::HashMap; use serde::Serialize; -use crate::templates::TemplateInfo; +use crate::TemplateInfo; -#[cfg(feature = "tera_templates")] use crate::templates::tera::Tera; -#[cfg(feature = "handlebars_templates")] use crate::templates::handlebars::Handlebars; +#[cfg(feature = "tera")] use crate::tera::Tera; +#[cfg(feature = "handlebars")] use crate::handlebars::Handlebars; pub(crate) trait Engine: Send + Sync + Sized + 'static { const EXT: &'static str; @@ -19,17 +19,17 @@ pub(crate) trait Engine: Send + Sync + Sized + 'static { /// /// Calling methods on the exposed template engine types may require importing /// types from the respective templating engine library. These types should be -/// imported from the reexported crate at the root of `rocket_contrib` to avoid -/// version mismatches. For instance, when registering a Tera filter, the +/// imported from the reexported crate at the root of `rocket_dyn_templates` to +/// avoid version mismatches. For instance, when registering a Tera filter, the /// [`tera::Value`] and [`tera::Result`] types are required. Import them from -/// `rocket_contrib::templates::tera`. The example below illustrates this: +/// `rocket_dyn_templates::tera`. The example below illustrates this: /// /// ```rust -/// # #[cfg(feature = "tera_templates")] { +/// # #[cfg(feature = "tera")] { /// use std::collections::HashMap; /// -/// use rocket_contrib::templates::{Template, Engines}; -/// use rocket_contrib::templates::tera::{self, Value}; +/// use rocket_dyn_templates::{Template, Engines}; +/// use rocket_dyn_templates::tera::{self, Value}; /// /// fn my_filter(value: &Value, _: &HashMap) -> tera::Result { /// # /* @@ -49,27 +49,27 @@ pub(crate) trait Engine: Send + Sync + Sized + 'static { /// # } /// ``` /// -/// [`tera::Value`]: crate::templates::tera::Value -/// [`tera::Result`]: crate::templates::tera::Result +/// [`tera::Value`]: crate::tera::Value +/// [`tera::Result`]: crate::tera::Result pub struct Engines { /// A `Tera` templating engine. This field is only available when the /// `tera_templates` feature is enabled. When calling methods on the `Tera` /// instance, ensure you use types imported from - /// `rocket_contrib::templates::tera` to avoid version mismatches. - #[cfg(feature = "tera_templates")] + /// `rocket_dyn_templates::tera` to avoid version mismatches. + #[cfg(feature = "tera")] pub tera: Tera, /// The Handlebars templating engine. This field is only available when the /// `handlebars_templates` feature is enabled. When calling methods on the /// `Tera` instance, ensure you use types imported from - /// `rocket_contrib::templates::handlebars` to avoid version mismatches. - #[cfg(feature = "handlebars_templates")] + /// `rocket_dyn_templates::handlebars` to avoid version mismatches. + #[cfg(feature = "handlebars")] pub handlebars: Handlebars<'static>, } impl Engines { pub(crate) const ENABLED_EXTENSIONS: &'static [&'static str] = &[ - #[cfg(feature = "tera_templates")] Tera::EXT, - #[cfg(feature = "handlebars_templates")] Handlebars::EXT, + #[cfg(feature = "tera")] Tera::EXT, + #[cfg(feature = "handlebars")] Handlebars::EXT, ]; pub(crate) fn init(templates: &HashMap) -> Option { @@ -83,12 +83,12 @@ impl Engines { } Some(Engines { - #[cfg(feature = "tera_templates")] + #[cfg(feature = "tera")] tera: match inner::(templates) { Some(tera) => tera, None => return None }, - #[cfg(feature = "handlebars_templates")] + #[cfg(feature = "handlebars")] handlebars: match inner::>(templates) { Some(hb) => hb, None => return None @@ -102,13 +102,13 @@ impl Engines { info: &TemplateInfo, context: C ) -> Option { - #[cfg(feature = "tera_templates")] { + #[cfg(feature = "tera")] { if info.engine_ext == Tera::EXT { return Engine::render(&self.tera, name, context); } } - #[cfg(feature = "handlebars_templates")] { + #[cfg(feature = "handlebars")] { if info.engine_ext == Handlebars::EXT { return Engine::render(&self.handlebars, name, context); } @@ -119,20 +119,24 @@ impl Engines { /// Returns iterator over template (name, engine_extension). pub(crate) fn templates(&self) -> impl Iterator { - #[cfg(all(feature = "tera_templates", feature = "handlebars_templates"))] { + #[cfg(all(feature = "tera", feature = "handlebars"))] { self.tera.get_template_names() .map(|name| (name, Tera::EXT)) .chain(self.handlebars.get_templates().keys() .map(|name| (name.as_str(), Handlebars::EXT))) } - #[cfg(all(feature = "tera_templates", not(feature = "handlebars_templates")))] { + #[cfg(all(feature = "tera", not(feature = "handlebars")))] { self.tera.get_template_names().map(|name| (name, Tera::EXT)) } - #[cfg(all(feature = "handlebars_templates", not(feature = "tera_templates")))] { + #[cfg(all(feature = "handlebars", not(feature = "tera")))] { self.handlebars.get_templates().keys() .map(|name| (name.as_str(), Handlebars::EXT)) } + + #[cfg(not(any(feature = "tera", feature = "handlebars")))] { + None.into_iter() + } } } diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/dyn_templates/src/fairing.rs similarity index 95% rename from contrib/lib/src/templates/fairing.rs rename to contrib/dyn_templates/src/fairing.rs index d34bb054..52aa2798 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/dyn_templates/src/fairing.rs @@ -1,5 +1,5 @@ -use crate::templates::{DEFAULT_TEMPLATE_DIR, Context, Engines}; -use crate::templates::context::{Callback, ContextManager}; +use crate::{DEFAULT_TEMPLATE_DIR, Context, Engines}; +use crate::context::{Callback, ContextManager}; use rocket::{Rocket, Build, Orbit}; use rocket::fairing::{self, Fairing, Info, Kind}; diff --git a/contrib/lib/src/templates/handlebars_templates.rs b/contrib/dyn_templates/src/handlebars_templates.rs similarity index 93% rename from contrib/lib/src/templates/handlebars_templates.rs rename to contrib/dyn_templates/src/handlebars_templates.rs index 45edea61..f36e7efd 100644 --- a/contrib/lib/src/templates/handlebars_templates.rs +++ b/contrib/dyn_templates/src/handlebars_templates.rs @@ -1,9 +1,9 @@ use std::path::Path; use serde::Serialize; -use crate::templates::Engine; -pub use crate::templates::handlebars::Handlebars; +use crate::engine::Engine; +pub use crate::handlebars::Handlebars; impl Engine for Handlebars<'static> { const EXT: &'static str = "hbs"; diff --git a/contrib/lib/src/templates/mod.rs b/contrib/dyn_templates/src/lib.rs similarity index 77% rename from contrib/lib/src/templates/mod.rs rename to contrib/dyn_templates/src/lib.rs index bd424341..c0d83339 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/dyn_templates/src/lib.rs @@ -1,50 +1,42 @@ -//! Dynamic template engine support for handlebars and tera. +//! Dynamic templating engine support for Rocket. //! -//! # Overview +//! This crate adds support for dynamic template rendering to Rocket. It +//! automatically discovers templates, provides a `Responder` to render +//! templates, and automatically reloads templates when compiled in debug mode. +//! At present, it supports [Handlebars] and [Tera]. //! -//! The general outline for using templates in Rocket is: +//! # Usage //! -//! 0. Enable the `rocket_contrib` feature corresponding to your templating -//! engine(s) of choice: +//! 1. Enable the `rocket_dyn_templates` feature corresponding to your +//! templating engine(s) of choice: //! //! ```toml -//! [dependencies.rocket_contrib] -//! version = "0.5.0-dev" +//! [dependencies.rocket_dyn_templates] +//! version = "0.1.0-dev" //! default-features = false -//! features = ["handlebars_templates", "tera_templates"] +//! features = ["handlebars", "tera"] //! ``` //! -//! 1. Write your template files in Handlebars (extension: `.hbs`) and/or tera -//! (extensions: `.tera`) in the templates directory (default: +//! 1. Write your template files in Handlebars (`.hbs`) and/or Tera (`.tera`) +//! in the configurable `template_dir` directory (default: //! `{rocket_root}/templates`). //! -//! 2. Attach the template fairing, [`Template::fairing()`]: -//! -//! ```rust -//! # extern crate rocket; -//! # extern crate rocket_contrib; -//! use rocket_contrib::templates::Template; -//! -//! fn main() { -//! rocket::build() -//! .attach(Template::fairing()) -//! // ... -//! # ; -//! } -//! ``` -//! -//! 3. Return a [`Template`] using [`Template::render()`], supplying the name -//! of the template file **minus the last two extensions**, from a handler. +//! 2. Attach `Template::fairing()` return a `Template` using +//! `Template::render()`, supplying the name of the template file **minus +//! the last two extensions**: //! //! ```rust //! # #[macro_use] extern crate rocket; -//! # #[macro_use] extern crate rocket_contrib; -//! # fn context() { } -//! use rocket_contrib::templates::Template; +//! use rocket_dyn_templates::Template; +//! +//! #[launch] +//! fn rocket() -> _ { +//! rocket::build().attach(Template::fairing()) +//! } //! //! #[get("/")] //! fn index() -> Template { -//! let context = context(); +//! # let context = (); //! Template::render("template-name", &context) //! } //! ``` @@ -57,7 +49,8 @@ //! [Discovery](#discovery) for more. //! //! Templates that are _not_ discovered by Rocket, such as those registered -//! directly via [`Template::custom()`], are _not_ renamed. +//! directly via [`Template::custom()`], are _not_ renamed. Use the name with +//! which the template was orginally registered. //! //! ## Content Type //! @@ -66,7 +59,7 @@ //! extension or the extension is unknown. For example, for a discovered //! template with file name `foo.html.hbs` or a manually registered template //! with name ending in `foo.html`, the `Content-Type` is automatically set to -//! [`ContentType::HTML`]. +//! `ContentType::HTML`. //! //! ## Discovery //! @@ -75,8 +68,8 @@ //! template directory is configured via the `template_dir` configuration //! parameter and defaults to `templates/`. The path set in `template_dir` is //! relative to the Rocket configuration file. See the [configuration -//! chapter](https://rocket.rs/master/guide/configuration/#extras) of the guide -//! for more information on configuration. +//! chapter](https://rocket.rs/master/guide/configuration) of the guide for more +//! information on configuration. //! //! The corresponding templating engine used for a given template is based on a //! template's extension. At present, this library supports the following @@ -93,9 +86,10 @@ //! 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 table illustrates this mapping: +//! minus at most two extensions. The following table contains examples of this +//! mapping: //! -//! | path | name | +//! | example template path | template name | //! |-----------------------------------------------|-----------------------| //! | {template_dir}/index.html.hbs | index | //! | {template_dir}/index.tera | index | @@ -109,32 +103,58 @@ //! type, and one for the template extension. This means that template //! extensions should look like: `.html.hbs`, `.html.tera`, `.xml.hbs`, etc. //! -//! ## Template Fairing +//! ## Template Fairing and Customization //! //! Template discovery is actualized by the template fairing, which itself is //! created via [`Template::fairing()`], [`Template::custom()`], or //! [`Template::try_custom()`], the latter two allowing customizations to -//! enabled templating engines. In order for _any_ templates to be rendered, the -//! template fairing _must_ be [attached](rocket::Rocket::attach()) to the -//! running Rocket instance. Failure to do so will result in a run-time error. +//! templating engines such as registering template helpers and register +//! templates from strings. +//! +//! In order for _any_ templates to be rendered, the template fairing _must_ be +//! [attached](rocket::Rocket::attach()) to the running Rocket instance. Failure +//! to do so will result in an ignite-time error. +//! +//! ## Rendering //! //! 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`] and would //! serialize to an `Object` value. //! -//! In debug mode (without the `--release` flag passed to `rocket`), templates +//! ## Automatic Reloading +//! +//! In debug mode (without the `--release` flag passed to `cargo`), templates //! will be automatically reloaded from disk if any changes have been made to //! the templates directory since the previous request. In release builds, //! template reloading is disabled to improve performance and cannot be enabled. //! //! [`Serialize`]: serde::Serialize -#[cfg(feature = "tera_templates")] pub extern crate tera; -#[cfg(feature = "tera_templates")] mod tera_templates; +#![doc(html_root_url = "https://api.rocket.rs/master/rocket_dyn_templates")] +#![doc(html_favicon_url = "https://rocket.rs/images/favicon.ico")] +#![doc(html_logo_url = "https://rocket.rs/images/logo-boxed.png")] -#[cfg(feature = "handlebars_templates")] pub extern crate handlebars; -#[cfg(feature = "handlebars_templates")] mod handlebars_templates; +#[macro_use] extern crate rocket; + +#[cfg(not(any(feature = "tera", feature = "handlebars")))] +compile_error!("at least one of \"tera\" or \"handlebars\" features must be enabled"); + +/// The tera templating engine library, reexported. +#[doc(inline)] +#[cfg(feature = "tera")] +pub use _tera as tera; + +#[cfg(feature = "tera")] +mod tera_templates; + +/// The handlebars templating engine library, reexported. +#[doc(inline)] +#[cfg(feature = "handlebars")] +pub use _handlebars as handlebars; + +#[cfg(feature = "handlebars")] +mod handlebars_templates; mod engine; mod fairing; @@ -144,7 +164,6 @@ mod metadata; pub use self::engine::Engines; pub use self::metadata::Metadata; -use self::engine::Engine; use self::fairing::TemplateFairing; use self::context::{Context, ContextManager}; @@ -169,56 +188,7 @@ const DEFAULT_TEMPLATE_DIR: &str = "templates"; /// contain the rendered template itself. The template is lazily rendered, at /// response time. To render a template greedily, use [`Template::show()`]. /// -/// # Usage -/// -/// To use, add the `handlebars_templates` feature, the `tera_templates` -/// feature, or both, to the `rocket_contrib` dependencies section of your -/// `Cargo.toml`: -/// -/// ```toml -/// [dependencies.rocket_contrib] -/// version = "0.5.0-dev" -/// default-features = false -/// features = ["handlebars_templates", "tera_templates"] -/// ``` -/// -/// Then, ensure that the template [`Fairing`] is attached to your Rocket -/// application: -/// -/// ```rust -/// # extern crate rocket; -/// # extern crate rocket_contrib; -/// use rocket_contrib::templates::Template; -/// -/// fn main() { -/// rocket::build() -/// .attach(Template::fairing()) -/// // ... -/// # ; -/// } -/// ``` -/// -/// The `Template` type implements Rocket's [`Responder`] trait, so it can be -/// returned from a request handler directly: -/// -/// ```rust -/// # #[macro_use] extern crate rocket; -/// # #[macro_use] extern crate rocket_contrib; -/// # fn context() { } -/// use rocket_contrib::templates::Template; -/// -/// #[get("/")] -/// fn index() -> Template { -/// let context = context(); -/// Template::render("index", &context) -/// } -/// ``` -/// -/// # Helpers, Filters, and Customization -/// -/// You may use the [`Template::custom()`] method to construct a fairing with -/// customized templating engines. Among other things, this method allows you to -/// register template helpers and register templates from strings. +/// See the [crate root](crate) for usage details. #[derive(Debug)] pub struct Template { name: Cow<'static, str>, @@ -254,9 +224,9 @@ impl Template { /// /// ```rust /// extern crate rocket; - /// extern crate rocket_contrib; + /// extern crate rocket_dyn_templates; /// - /// use rocket_contrib::templates::Template; + /// use rocket_dyn_templates::Template; /// /// fn main() { /// rocket::build() @@ -283,9 +253,9 @@ impl Template { /// /// ```rust /// extern crate rocket; - /// extern crate rocket_contrib; + /// extern crate rocket_dyn_templates; /// - /// use rocket_contrib::templates::Template; + /// use rocket_dyn_templates::Template; /// /// fn main() { /// rocket::build() @@ -314,9 +284,9 @@ impl Template { /// /// ```rust /// extern crate rocket; - /// extern crate rocket_contrib; + /// extern crate rocket_dyn_templates; /// - /// use rocket_contrib::templates::Template; + /// use rocket_dyn_templates::Template; /// /// fn main() { /// rocket::build() @@ -343,7 +313,7 @@ impl Template { /// /// ```rust /// use std::collections::HashMap; - /// use rocket_contrib::templates::Template; + /// use rocket_dyn_templates::Template; /// /// // Create a `context`. Here, just an empty `HashMap`. /// let mut context = HashMap::new(); @@ -376,10 +346,10 @@ impl Template { /// /// ```rust,no_run /// # extern crate rocket; - /// # extern crate rocket_contrib; + /// # extern crate rocket_dyn_templates; /// use std::collections::HashMap; /// - /// use rocket_contrib::templates::Template; + /// use rocket_dyn_templates::Template; /// use rocket::local::blocking::Client; /// /// fn main() { diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/dyn_templates/src/metadata.rs similarity index 91% rename from contrib/lib/src/templates/metadata.rs rename to contrib/dyn_templates/src/metadata.rs index aa44194b..b119c129 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/dyn_templates/src/metadata.rs @@ -2,7 +2,7 @@ use rocket::{Request, Rocket, Ignite, Sentinel}; use rocket::http::Status; use rocket::request::{self, FromRequest}; -use crate::templates::context::ContextManager; +use crate::context::ContextManager; /// Request guard for dynamically querying template metadata. /// @@ -13,8 +13,8 @@ use crate::templates::context::ContextManager; /// /// ```rust /// # #[macro_use] extern crate rocket; -/// # #[macro_use] extern crate rocket_contrib; -/// use rocket_contrib::templates::{Template, Metadata}; +/// # #[macro_use] extern crate rocket_dyn_templates; +/// use rocket_dyn_templates::{Template, Metadata}; /// /// #[get("/")] /// fn homepage(metadata: Metadata) -> Template { @@ -45,9 +45,9 @@ impl Metadata<'_> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// # extern crate rocket_contrib; + /// # extern crate rocket_dyn_templates; /// # - /// use rocket_contrib::templates::Metadata; + /// use rocket_dyn_templates::Metadata; /// /// #[get("/")] /// fn handler(metadata: Metadata) { @@ -65,9 +65,9 @@ impl Metadata<'_> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// # extern crate rocket_contrib; + /// # extern crate rocket_dyn_templates; /// # - /// use rocket_contrib::templates::Metadata; + /// use rocket_dyn_templates::Metadata; /// /// #[get("/")] /// fn handler(metadata: Metadata) { diff --git a/contrib/lib/src/templates/tera_templates.rs b/contrib/dyn_templates/src/tera_templates.rs similarity index 96% rename from contrib/lib/src/templates/tera_templates.rs rename to contrib/dyn_templates/src/tera_templates.rs index 7772651e..a98bb92d 100644 --- a/contrib/lib/src/templates/tera_templates.rs +++ b/contrib/dyn_templates/src/tera_templates.rs @@ -2,9 +2,10 @@ use std::path::Path; use std::error::Error; use serde::Serialize; -use crate::templates::Engine; -pub use crate::templates::tera::{Context, Tera}; +use crate::engine::Engine; + +pub use crate::tera::{Context, Tera}; impl Engine for Tera { const EXT: &'static str = "tera"; diff --git a/contrib/dyn_templates/tests/templates.rs b/contrib/dyn_templates/tests/templates.rs new file mode 100644 index 00000000..1d91abb0 --- /dev/null +++ b/contrib/dyn_templates/tests/templates.rs @@ -0,0 +1,238 @@ +#[macro_use] extern crate rocket; + +use std::path::{Path, PathBuf}; + +use rocket::{Rocket, Build}; +use rocket::config::Config; +use rocket_dyn_templates::{Template, Metadata}; + +#[get("//")] +fn template_check(md: Metadata<'_>, engine: &str, name: &str) -> Option<()> { + match md.contains_template(&format!("{}/{}", engine, name)) { + true => Some(()), + false => None + } +} + +#[get("/is_reloading")] +fn is_reloading(md: Metadata<'_>) -> Option<()> { + if md.reloading() { Some(()) } else { None } +} + +fn template_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests").join("templates") +} + +fn rocket() -> Rocket { + rocket::custom(Config::figment().merge(("template_dir", template_root()))) + .attach(Template::fairing()) + .mount("/", routes![template_check, is_reloading]) +} + +#[test] +fn test_callback_error() { + use rocket::{local::blocking::Client, error::ErrorKind::FailedFairings}; + + let rocket = rocket::build().attach(Template::try_custom(|_| { + Err("error reloading templates!".into()) + })); + + let error = Client::debug(rocket).expect_err("client failure"); + match error.kind() { + FailedFairings(failures) => assert_eq!(failures[0].name, "Templating"), + _ => panic!("Wrong kind of launch error"), + } +} + +#[test] +fn test_sentinel() { + use rocket::{local::blocking::Client, error::ErrorKind::SentinelAborts}; + + let err = Client::debug_with(routes![is_reloading]).unwrap_err(); + assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1)); + + let err = Client::debug_with(routes![is_reloading, template_check]).unwrap_err(); + assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2)); + + #[get("/")] + fn return_template() -> Template { + Template::render("foo", ()) + } + + let err = Client::debug_with(routes![return_template]).unwrap_err(); + assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1)); + + #[get("/")] + fn return_opt_template() -> Option