diff options
author | René Kijewski <kijewski@library.vetmed.fu-berlin.de> | 2022-07-20 21:18:18 +0200 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2022-07-25 11:39:58 +0200 |
commit | bd8d2f334ed2a9fd0fb1bb6f9d87b4ab45556918 (patch) | |
tree | fdca7dca4591f62fca30d798b37779f6fadae00a /askama_hyper | |
parent | c5fbd2ebfb0ce84a49db47f09caa12a048ea61d0 (diff) | |
download | askama-bd8d2f334ed2a9fd0fb1bb6f9d87b4ab45556918.tar.gz askama-bd8d2f334ed2a9fd0fb1bb6f9d87b4ab45556918.tar.bz2 askama-bd8d2f334ed2a9fd0fb1bb6f9d87b4ab45556918.zip |
Implement basic hyper integration
The integration is based on askama_gotham.
There is no specific trait to convert an arbitrary T to
`hyper::Response`, so I used `From<Template> for hyper::Response`.
Diffstat (limited to '')
-rw-r--r-- | askama_hyper/Cargo.toml | 33 | ||||
l--------- | askama_hyper/LICENSE-APACHE | 1 | ||||
l--------- | askama_hyper/LICENSE-MIT | 1 | ||||
-rw-r--r-- | askama_hyper/README.md | 9 | ||||
-rw-r--r-- | askama_hyper/src/lib.rs | 29 | ||||
-rw-r--r-- | askama_hyper/templates/hello.html | 1 | ||||
-rw-r--r-- | askama_hyper/tests/basic.rs | 70 |
7 files changed, 144 insertions, 0 deletions
diff --git a/askama_hyper/Cargo.toml b/askama_hyper/Cargo.toml new file mode 100644 index 0000000..6a7a213 --- /dev/null +++ b/askama_hyper/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "askama_hyper" +version = "0.1.0" +description = "Hyper integration for Askama templates" +documentation = "https://docs.rs/askama" +keywords = ["markup", "template", "jinja2", "html"] +categories = ["template-engine"] +homepage = "https://github.com/djc/askama" +repository = "https://github.com/djc/askama" +license = "MIT OR Apache-2.0" +workspace = ".." +readme = "README.md" +edition = "2018" + +[dependencies] +askama = { version = "0.11.2", path = "../askama", default-features = false, features = ["with-hyper"] } +hyper = { version = "0.14", default-features = false } + +[dev-dependencies] +hyper = { version = "0.14", features = ["client"] } +mime = "0.3" +routerify = "3" +tokio = { version = "1", default-features = false, features = ["net", "macros", "rt"] } + +[features] +default = ["askama/default"] +config = ["askama/config"] +humansize = ["askama/humansize"] +markdown = ["askama/markdown"] +num-traits = ["askama/num-traits"] +serde-json = ["askama/serde-json"] +serde-yaml = ["askama/serde-yaml"] +urlencode = ["askama/urlencode"] diff --git a/askama_hyper/LICENSE-APACHE b/askama_hyper/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/askama_hyper/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE
\ No newline at end of file diff --git a/askama_hyper/LICENSE-MIT b/askama_hyper/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/askama_hyper/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT
\ No newline at end of file diff --git a/askama_hyper/README.md b/askama_hyper/README.md new file mode 100644 index 0000000..024665a --- /dev/null +++ b/askama_hyper/README.md @@ -0,0 +1,9 @@ +# askama_hyper: Askama integration with Hyper + +[![Documentation](https://docs.rs/askama_hyper/badge.svg)](https://docs.rs/askama_hyper/) +[![Latest version](https://img.shields.io/crates/v/askama_hyper.svg)](https://crates.io/crates/askama_hyper) +[![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) +[![Chat](https://badges.gitter.im/gitterHQ/gitter.svg)](https://gitter.im/djc/askama) + +Integration of the [Askama](https://github.com/djc/askama) templating engine in +code building on the [hyper](https://crates.io/crates/hyper)-based web servers. diff --git a/askama_hyper/src/lib.rs b/askama_hyper/src/lib.rs new file mode 100644 index 0000000..07ede88 --- /dev/null +++ b/askama_hyper/src/lib.rs @@ -0,0 +1,29 @@ +#![forbid(unsafe_code)] +#![deny(elided_lifetimes_in_paths)] +#![deny(unreachable_pub)] + +pub use askama::*; +#[doc(hidden)] +pub use hyper; +use hyper::{header, Body, Response, StatusCode}; + +pub fn try_respond<T: Template>(t: &T, _ext: &str) -> Result<Response<Body>> { + Response::builder() + .status(StatusCode::OK) + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static(T::MIME_TYPE), + ) + .body(t.render()?.into()) + .map_err(|err| Error::Custom(Box::new(err))) +} + +pub fn respond<T: Template>(t: &T, _ext: &str) -> Response<Body> { + match try_respond(t, _ext) { + Ok(response) => response, + Err(_) => Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::empty()) + .unwrap(), + } +} diff --git a/askama_hyper/templates/hello.html b/askama_hyper/templates/hello.html new file mode 100644 index 0000000..8149be7 --- /dev/null +++ b/askama_hyper/templates/hello.html @@ -0,0 +1 @@ +Hello, {{ name }}! diff --git a/askama_hyper/tests/basic.rs b/askama_hyper/tests/basic.rs new file mode 100644 index 0000000..d0038c1 --- /dev/null +++ b/askama_hyper/tests/basic.rs @@ -0,0 +1,70 @@ +use std::convert::Infallible; + +use askama::Template; +use hyper::body::to_bytes; +use hyper::{Body, Client, Request, Response, Server}; +use routerify::ext::RequestExt; +use routerify::{Router, RouterService}; + +#[derive(Template)] +#[template(path = "hello.html")] +struct HelloTemplate<'a, N> +where + N: std::fmt::Display, +{ + name: &'a N, +} + +async fn hello_handler(req: Request<Body>) -> Result<Response<Body>, Infallible> { + let name = req.param("name").unwrap(); + let template = &HelloTemplate { name: &name }; + Ok(template.into()) +} + +fn router() -> Router<Body, Infallible> { + Router::builder() + .get("/hello/:name", hello_handler) + .build() + .unwrap() +} + +#[tokio::test] +async fn test_hyper() { + let addr = ([127, 0, 0, 1], 0).into(); + let service = RouterService::new(router()).expect("Could not create service"); + let server = Server::bind(&addr).serve(service); + let local_addr = server.local_addr(); + + let (tx, rx) = tokio::sync::oneshot::channel::<()>(); + let serve = async move { + let server = server.with_graceful_shutdown(async { + rx.await.expect("Could not await signal to stop"); + }); + server.await.expect("Could not serve"); + }; + let query = async move { + let uri = format!("http://{}/hello/world", local_addr) + .parse() + .expect("Could not format URI"); + let client = Client::new(); + + let res = client.get(uri).await.expect("Could not query client"); + assert_eq!(res.status(), hyper::StatusCode::OK); + + let content_type = res + .headers() + .get("content-type") + .expect("Response did not contain content-type header") + .to_str() + .expect("Content-type was not a UTF-8 string"); + assert_eq!(content_type, mime::TEXT_HTML_UTF_8.to_string()); + + let body = to_bytes(res).await.expect("No body returned"); + let body = std::str::from_utf8(&body).expect("Body was not UTF-8"); + assert_eq!(body, "Hello, world!"); + + tx.send(()).unwrap(); + }; + + tokio::join!(serve, query); +} |