1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
/*!
# Include Handlebars Templates for Rocket Framework
This is a crate which provides macros `handlebars_resources_initialize!` and `handlebars_response!` to statically include HBS (Handlebars) files from your Rust project and make them be the HTTP response sources quickly.
## Example
```rust
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate lazy_static_include;
#[macro_use] extern crate handlebars;
#[macro_use] extern crate rocket_include_handlebars;
#[macro_use] extern crate rocket;
use std::collections::HashMap;
use rocket_include_handlebars::{EtagIfNoneMatch, HandlebarsResponse};
handlebars_resources_initialize!(
"index", "examples/views/index.hbs",
"index2", "examples/views/index2.hbs"
);
#[get("/")]
fn index() -> HandlebarsResponse {
let mut map = HashMap::new();
map.insert("title", "Title");
map.insert("body", "Hello, world!");
handlebars_response!("index", &map)
}
#[get("/2")]
fn index_2() -> HandlebarsResponse {
let mut map = HashMap::new();
map.insert("title", "Title");
map.insert("body", "Hello, world!");
handlebars_response!("index2", &map)
}
#[get("/static")]
fn index_static() -> HandlebarsResponse {
handlebars_response_static!(
"index".to_string(),
{
let mut map = HashMap::new();
map.insert("title", "Title");
map.insert("body", "Hello, world!");
handlebars_response!("index", &map)
}
)
}
```
* `handlebars_resources_initialize!` is used for including HBS files into your executable binary file. You need to specify each file's ID and its path. For instance, the above example uses **index** to represent the file **included-handlebars/index.hbs** and **index-2** to represent the file **included-handlebars/index2.hbs**. An ID cannot be repeating.
* `handlebars_response!` is used for retrieving and rendering the file you input through the macro `handlebars_resources_initialize!` as a `HandlebarsResponse` instance with rendered HTML. When its `respond_to` method is called, three HTTP headers, **Content-Type**, **Content-Length** and **Etag**, will be automatically added, and the rendered HTML can optionally be minified.
* `handlebars_response_static!` is used for in-memory staticizing a `HandlebarsResponse` instance by a given key.
In order to reduce the compilation time, files are compiled into your executable binary file together, only when you are using the **release** profile.
See `examples`.
*/
#[doc(hidden)]
pub extern crate handlebars;
#[doc(hidden)]
pub extern crate crc_any;
#[doc(hidden)]
pub extern crate html_minifier;
extern crate rocket;
extern crate rocket_etag_if_none_match;
use crc_any::CRC;
use rocket::request::Request;
use rocket::response::{self, Response, Responder};
use rocket::http::{Status, hyper::header::ETag};
use std::io::Cursor;
pub use rocket_etag_if_none_match::{EntityTag, EtagIfNoneMatch};
pub struct HandlebarsResponse {
/// The HTML code.
pub html: String,
/// The Etag from HTTP client (Etag-If-None-Match).
pub client_etag: EtagIfNoneMatch,
/// The Etag computed from the HTML code or provided by a specific one.
pub etag: Option<EntityTag>,
/// If you don't want minify the rendered HTML, set `minify` to **false**.
pub minify: bool,
}
impl<'a> Responder<'a> for HandlebarsResponse {
fn respond_to(self, _: &Request) -> response::Result<'a> {
let etag = match self.etag {
Some(etag) => etag,
None => {
let mut crc64ecma = CRC::crc64ecma();
crc64ecma.digest(self.html.as_bytes());
let crc64 = crc64ecma.get_crc();
EntityTag::new(true, format!("{:X}", crc64))
}
};
let is_etag_match = self.client_etag.weak_eq(&etag);
let mut response = Response::build();
if is_etag_match {
response.status(Status::NotModified);
} else {
let html = if self.minify {
html_minifier::minify(&self.html).unwrap()
} else {
self.html
};
response
.header(ETag(etag))
.raw_header("Content-Type", "text/html")
.raw_header("Content-Length", html.len().to_string())
.sized_body(Cursor::new(html));
}
response.ok()
}
}
mod macros;