aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kellen Frodelius-Fujimoto <kellen@kellenfujimoto.com>2018-12-10 20:21:15 +0100
committerLibravatar Juan Aguilar <mhpoin@gmail.com>2018-12-10 22:29:24 +0100
commit9b0001cdf3991e74c042a9661e306b13785ca223 (patch)
treed8ccfbdec0c5ad4d81150b92c2c74952abfc5da2
parent5549f9a3cd94e3cd6700067b1c74194dadb58a0f (diff)
downloadaskama-9b0001cdf3991e74c042a9661e306b13785ca223.tar.gz
askama-9b0001cdf3991e74c042a9661e306b13785ca223.tar.bz2
askama-9b0001cdf3991e74c042a9661e306b13785ca223.zip
Implement `IntoResponse` for `Template`, allowing them to be used in `gotham` handlers.
-rw-r--r--README.md2
-rw-r--r--askama/Cargo.toml3
-rw-r--r--askama/src/lib.rs35
-rw-r--r--askama_derive/Cargo.toml1
-rw-r--r--askama_derive/src/generator.rs19
-rw-r--r--testing/Cargo.toml4
-rw-r--r--testing/tests/gotham.rs39
7 files changed, 102 insertions, 1 deletions
diff --git a/README.md b/README.md
index 8924c5e..129ad4a 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/askama/Cargo.toml b/askama/Cargo.toml
index 7c85a92..22d129d 100644
--- a/askama/Cargo.toml
+++ b/askama/Cargo.toml
@@ -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" }
@@ -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" ]
diff --git a/askama/src/lib.rs b/askama/src/lib.rs
index 7381af7..d1bb639 100644
--- a/askama/src/lib.rs
+++ b/askama/src/lib.rs
@@ -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.
@@ -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)? {
diff --git a/askama_derive/Cargo.toml b/askama_derive/Cargo.toml
index 7096597..461a1f8 100644
--- a/askama_derive/Cargo.toml
+++ b/askama_derive/Cargo.toml
@@ -16,6 +16,7 @@ proc-macro = true
iron = []
rocket = []
actix-web = []
+gotham = []
[dependencies]
askama_shared = { version = "0.7.2", path = "../askama_shared" }
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 61a82bd..b30b040 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -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
}
@@ -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(
diff --git a/testing/Cargo.toml b/testing/Cargo.toml
index 16d2f34..720ef2e 100644
--- a/testing/Cargo.toml
+++ b/testing/Cargo.toml
@@ -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 }
@@ -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 = "*" }
diff --git a/testing/tests/gotham.rs b/testing/tests/gotham.rs
new file mode 100644
index 0000000..f127d59
--- /dev/null
+++ b/testing/tests/gotham.rs
@@ -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!");
+}