Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional Gotham support #177

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ in a for-profit context, please consider supporting my open source work on
* Construct templates using a familiar, easy-to-use syntax
* Template code is compiled into your crate for [optimal performance][benchmarks]
* Benefit from the safety provided by Rust's type system
* Optional built-in support for Actix, Iron and Rocket web frameworks
* Optional built-in support for Actix, Gotham, Iron and Rocket web frameworks
* Debugging features to assist you in template development
* Templates must be valid UTF-8 and produce UTF-8 when rendered
* Works on stable Rust
Expand Down
3 changes: 3 additions & 0 deletions askama/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ serde-json = ["askama_shared/serde_json"]
with-iron = ["iron", "askama_derive/iron"]
with-rocket = ["rocket", "askama_derive/rocket"]
with-actix-web = ["actix-web", "askama_derive/actix-web", "mime_guess"]
with-gotham = ["gotham", "askama_derive/gotham", "hyper", "mime_guess"]

[dependencies]
askama_derive = { version = "0.8.0", path = "../askama_derive" }
Expand All @@ -32,6 +33,8 @@ iron = { version = ">= 0.5, < 0.7", optional = true }
rocket = { version = "0.4", optional = true }
actix-web = { version = "0.7", optional = true }
mime_guess = { version = "2.0.0-alpha", optional = true }
gotham = { version = "0.3", optional = true }
hyper = { version = "0.12", optional = true }

[package.metadata.docs.rs]
features = [ "serde-json" ]
35 changes: 35 additions & 0 deletions askama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,16 @@
//! `Responder` trait for each template type. This makes it easy to return a value of
//! that type in an Actix-web handler.
//!
//! ## Gotham integration
//!
//! Enabling the `with-gotham` feature appends an implementation of Gotham's `IntoResponse`
//! trait for each template type. This makes it easy to return a value of that type in a
//! Gotham handler.
//!
//! In case of a run-time error occurring during templating, the response will be of the same
//! signature, with a status code of `500 Internal Server Error`, mime `*/*`, and an empty `Body`.
//! This preserves the response chain if any custom error handling needs to occur.
//!
//! ## The `json` filter
//!
//! Enabling the `serde-json` filter will enable the use of the `json` filter.
Expand Down Expand Up @@ -478,6 +488,31 @@ pub mod actix_web {
}
}

#[cfg(feature = "with-gotham")]
pub mod gotham {
pub use gotham::handler::IntoResponse;
use gotham::helpers::http::response::{create_empty_response, create_response};
pub use gotham::state::State;
pub use hyper::{Body, Response, StatusCode};
use mime_guess::get_mime_type;

pub fn respond(t: &super::Template, ext: &str) -> Response<Body> {
let mime_type = get_mime_type(ext).to_string();

match t.render() {
Ok(body) => Response::builder()
.status(StatusCode::OK)
.header("content-type", mime_type.to_string())
.body(body.into())
.unwrap(),
Err(_) => Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(vec![].into())
.unwrap(),
}
}
}

fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
Expand Down
1 change: 1 addition & 0 deletions askama_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ proc-macro = true
iron = []
rocket = []
actix-web = []
gotham = []

[dependencies]
askama_shared = { version = "0.7.2", path = "../askama_shared" }
Expand Down
19 changes: 19 additions & 0 deletions askama_derive/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ impl<'a> Generator<'a> {
if cfg!(feature = "actix-web") {
self.impl_actix_web_responder(&mut buf);
}
if cfg!(feature = "gotham") {
self.impl_gotham_into_response(&mut buf);
}
buf.buf
}

Expand Down Expand Up @@ -217,6 +220,22 @@ impl<'a> Generator<'a> {
buf.writeln("}");
}

// Implement gotham's `IntoResponse`.
fn impl_gotham_into_response(&mut self, buf: &mut Buffer) {
self.write_header(buf, "::askama::gotham::IntoResponse", None);
buf.writeln(
"fn into_response(self, _state: &::askama::gotham::State)\
-> ::askama::gotham::Response<::askama::gotham::Body> {",
);
let ext = match self.input.path.extension() {
Some(s) => s.to_str().unwrap(),
None => "txt",
};
buf.writeln(&format!("::askama::gotham::respond(&self, {:?})", ext));
buf.writeln("}");
buf.writeln("}");
}

// Writes header for the `impl` for `TraitFromPathName` or `Template`
// for the given context struct.
fn write_header(
Expand Down
4 changes: 4 additions & 0 deletions testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ full = ["actix", "with-iron", "serde-json"]
serde-json = ["serde_json", "askama/serde-json"]
with-rocket = ["rocket", "askama/with-rocket"]
with-iron = ["iron", "askama/with-iron"]
with-gotham = ["gotham", "askama/with-gotham", "mime", "hyper"]

[dependencies]
actix-web = { version = "0.7", optional = true }
Expand All @@ -20,6 +21,9 @@ bytes = { version = "0.4", optional = true }
iron = { version = "0.6", optional = true }
rocket = { version = "0.4", optional = true }
serde_json = { version = "1.0", optional = true }
gotham = { version = "0.3", optional = true }
mime = { version = "0.3", optional = true }
hyper = { version = "0.12", optional = true }

[build-dependencies]
askama = { path = "../askama", version = "*" }
Expand Down
39 changes: 39 additions & 0 deletions testing/tests/gotham.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#![cfg(feature = "with-gotham")]

use askama::Template;
use gotham::state::State;
use gotham::test::TestServer;
use hyper::StatusCode;

#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> {
name: &'a str,
}

fn hello(state: State) -> (State, HelloTemplate<'static>) {
(state, HelloTemplate { name: "world" })
}

#[test]
fn test_gotham() {
let test_server = TestServer::new(|| Ok(hello)).expect("Failed to mount test router");

let res = test_server
.client()
.get("http://localhost/")
.perform()
.expect("Failed to send request to gotham");

assert_eq!(res.status(), StatusCode::OK);
{
let headers = res.headers();
let content_type = headers
.get("content-type")
.expect("Response did not contain content-type header");
assert_eq!(content_type.to_str().unwrap(), mime::TEXT_HTML.to_string());
}

let body = res.read_utf8_body().expect("failed to read response body");
assert_eq!(&body, "Hello, world!");
}