From d3b048544eebf7b8402fb585b4e2531de9b28a33 Mon Sep 17 00:00:00 2001 From: cel 🌸 Date: Wed, 24 Jan 2024 20:23:18 +0000 Subject: Add poem integration --- .github/workflows/rust.yml | 2 +- Cargo.toml | 1 + README.md | 2 +- askama/Cargo.toml | 1 + askama_derive/Cargo.toml | 1 + askama_derive/src/generator.rs | 16 ++++++++++++++++ askama_poem/Cargo.toml | 32 ++++++++++++++++++++++++++++++++ askama_poem/LICENSE-APACHE | 1 + askama_poem/LICENSE-MIT | 1 + askama_poem/README.md | 9 +++++++++ askama_poem/src/lib.rs | 17 +++++++++++++++++ askama_poem/templates/hello.html | 1 + askama_poem/tests/basic.rs | 23 +++++++++++++++++++++++ book/src/askama.md | 2 +- book/src/integrations.md | 12 ++++++++++++ 15 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 askama_poem/Cargo.toml create mode 120000 askama_poem/LICENSE-APACHE create mode 120000 askama_poem/LICENSE-MIT create mode 100644 askama_poem/README.md create mode 100644 askama_poem/src/lib.rs create mode 100644 askama_poem/templates/hello.html create mode 100644 askama_poem/tests/basic.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 759edb3..78f91ec 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -33,7 +33,7 @@ jobs: matrix: package: [ askama, askama_actix, askama_axum, askama_derive, askama_escape, askama_gotham, - askama_hyper, askama_mendes, askama_parser, askama_rocket, askama_tide, askama_warp, + askama_hyper, askama_mendes, askama_parser, askama_poem, askama_rocket, askama_tide, askama_warp, testing, ] runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index e7fd38f..09ab91c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "askama_escape", "askama_mendes", "askama_parser", + "askama_poem", "askama_rocket", "askama_tide", "askama_warp", diff --git a/README.md b/README.md index f394dfb..e17a4dd 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ in a for-profit context, please consider supporting my open source work on * Construct templates using a familiar, easy-to-use syntax * Benefit from the safety provided by Rust's type system * Template code is compiled into your crate for [optimal performance][benchmarks] -* Optional built-in support for Actix, Axum, Gotham, Mendes, Rocket, tide, and warp web frameworks +* Optional built-in support for Actix, Axum, Gotham, Mendes, Poem, Rocket, tide, and warp web frameworks * Debugging features to assist you in template development * Templates must be valid UTF-8 and produce UTF-8 when rendered * IDE support available in [JetBrains products](https://plugins.jetbrains.com/plugin/16591-askama-template-support) diff --git a/askama/Cargo.toml b/askama/Cargo.toml index 26be2a4..177b93d 100644 --- a/askama/Cargo.toml +++ b/askama/Cargo.toml @@ -30,6 +30,7 @@ with-axum = ["askama_derive/with-axum"] with-gotham = ["askama_derive/with-gotham"] with-hyper = ["askama_derive/with-hyper"] with-mendes = ["askama_derive/with-mendes"] +with-poem = ["askama_derive/with-poem"] with-rocket = ["askama_derive/with-rocket"] with-tide = ["askama_derive/with-tide"] with-warp = ["askama_derive/with-warp"] diff --git a/askama_derive/Cargo.toml b/askama_derive/Cargo.toml index 183958a..7cc830a 100644 --- a/askama_derive/Cargo.toml +++ b/askama_derive/Cargo.toml @@ -26,6 +26,7 @@ with-axum = [] with-gotham = [] with-hyper = [] with-mendes = [] +with-poem = [] with-rocket = [] with-tide = [] with-warp = [] diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 22f996f..1a2537e 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -74,6 +74,8 @@ impl<'a> Generator<'a> { self.impl_hyper_into_response(&mut buf)?; #[cfg(feature = "with-mendes")] self.impl_mendes_responder(&mut buf)?; + #[cfg(feature = "with-poem")] + self.impl_poem_into_response(&mut buf)?; #[cfg(feature = "with-rocket")] self.impl_rocket_responder(&mut buf)?; #[cfg(feature = "with-tide")] @@ -284,6 +286,20 @@ impl<'a> Generator<'a> { Ok(()) } + // Implement Poem's `IntoResponse`. + #[cfg(feature = "with-poem")] + fn impl_poem_into_response(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { + self.write_header(buf, "::askama_poem::IntoResponse", None)?; + buf.writeln("#[inline]")?; + buf.writeln( + "fn into_response(self)\ + -> ::askama_poem::Response {", + )?; + buf.writeln("::askama_poem::into_response(&self)")?; + buf.writeln("}")?; + buf.writeln("}") + } + // Implement Rocket's `Responder`. #[cfg(feature = "with-rocket")] fn impl_rocket_responder(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { diff --git a/askama_poem/Cargo.toml b/askama_poem/Cargo.toml new file mode 100644 index 0000000..20c720b --- /dev/null +++ b/askama_poem/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "askama_poem" +version = "0.14.0" +description = "Poem 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 = "2021" +rust-version = "1.65" + +[dependencies] +poem = { version = "2.0.0", default-features = false } +askama = { version = "0.12", path = "../askama", default-features = false, features = ["with-poem"] } + +[dev-dependencies] +poem = { version = "2.0.0", features = ["test"] } +tokio = "1.0" + +[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_poem/LICENSE-APACHE b/askama_poem/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/askama_poem/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/askama_poem/LICENSE-MIT b/askama_poem/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/askama_poem/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/askama_poem/README.md b/askama_poem/README.md new file mode 100644 index 0000000..0aecdea --- /dev/null +++ b/askama_poem/README.md @@ -0,0 +1,9 @@ +# askama_poem: Askama integration with Poem + +[![Documentation](https://docs.rs/askama_poem/badge.svg)](https://docs.rs/askama_poem/) +[![Latest version](https://img.shields.io/crates/v/askama_poem.svg)](https://crates.io/crates/askama_poem) +[![Build Status](https://github.com/djc/askama/workflows/CI/badge.svg)](https://github.com/djc/askama/actions?query=workflow%3ACI) +[![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/ZucwjE6bmT) + +Integration of the [Askama](https://github.com/djc/askama) templating engine in +code building on the poem web framework. diff --git a/askama_poem/src/lib.rs b/askama_poem/src/lib.rs new file mode 100644 index 0000000..de69e09 --- /dev/null +++ b/askama_poem/src/lib.rs @@ -0,0 +1,17 @@ +#![forbid(unsafe_code)] +#![deny(elided_lifetimes_in_paths)] +#![deny(unreachable_pub)] + +pub use askama::*; +use poem::http::StatusCode; +pub use poem::{web::IntoResponse, Response}; + +pub fn into_response(t: &T) -> Response { + match t.render() { + Ok(body) => Response::builder() + .status(StatusCode::OK) + .content_type(T::MIME_TYPE) + .body(body), + Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into(), + } +} diff --git a/askama_poem/templates/hello.html b/askama_poem/templates/hello.html new file mode 100644 index 0000000..8149be7 --- /dev/null +++ b/askama_poem/templates/hello.html @@ -0,0 +1 @@ +Hello, {{ name }}! diff --git a/askama_poem/tests/basic.rs b/askama_poem/tests/basic.rs new file mode 100644 index 0000000..406c45f --- /dev/null +++ b/askama_poem/tests/basic.rs @@ -0,0 +1,23 @@ +use askama::Template; +use poem::{handler, test::TestClient, Route}; + +#[derive(Template)] +#[template(path = "hello.html")] +struct HelloTemplate<'a> { + name: &'a str, +} + +#[handler] +fn hello() -> HelloTemplate<'static> { + HelloTemplate { name: "world" } +} + +#[tokio::test] +async fn test_poem() { + let app = Route::new().at("/", hello); + let cli = TestClient::new(app); + + let resp = cli.get("/").send().await; + resp.assert_status_is_ok(); + resp.assert_text("Hello, world!").await; +} diff --git a/book/src/askama.md b/book/src/askama.md index 2eb366c..74068f0 100644 --- a/book/src/askama.md +++ b/book/src/askama.md @@ -26,7 +26,7 @@ in a for-profit context, please consider supporting my open source work on * Construct templates using a familiar, easy-to-use syntax * Benefit from the safety provided by Rust's type system * Template code is compiled into your crate for [optimal performance][benchmarks] -* Optional built-in support for Actix, Axum, Gotham, Mendes, Rocket, tide, and warp web frameworks +* Optional built-in support for Actix, Axum, Gotham, Mendes, Poem, Rocket, tide, and warp web frameworks * Debugging features to assist you in template development * Templates must be valid UTF-8 and produce UTF-8 when rendered * IDE support available in [JetBrains products](https://plugins.jetbrains.com/plugin/16591-askama-template-support) diff --git a/book/src/integrations.md b/book/src/integrations.md index 918b887..3d6d508 100644 --- a/book/src/integrations.md +++ b/book/src/integrations.md @@ -44,6 +44,18 @@ In case of a run-time error occurring during templating, the response will be of 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. +## Poem integration + +Enabling the `with-poem` feature appends an implementation of Poem's +`IntoResponse` trait for each template type. This makes it easy to trivially +return a value of that type in a Poem handler. See +[the example](https://github.com/djc/askama/blob/main/askama_poem/tests/basic.rs) +from the Askama test suite for more on how to integrate. + +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. + ## Warp integration Enabling the `with-warp` feature appends an implementation of Warp's `Reply` -- cgit