From b56c11639f9ea5ef1354a1e91ca98541a16bca9b Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 28 Jan 2020 21:26:13 +0100 Subject: Move Actix-Web integration into separate askama_actix crate --- .github/workflows/rust.yml | 29 ++++++++------ Cargo.toml | 16 +++++++- askama/Cargo.toml | 5 +-- askama/src/lib.rs | 82 ++++++++++++--------------------------- askama_actix/Cargo.toml | 22 +++++++++++ askama_actix/src/lib.rs | 22 +++++++++++ askama_actix/templates/hello.html | 1 + askama_actix/tests/basic.rs | 53 +++++++++++++++++++++++++ askama_derive/src/generator.rs | 10 ++--- testing/Cargo.toml | 7 +--- testing/tests/actix_web.rs | 54 -------------------------- 11 files changed, 163 insertions(+), 138 deletions(-) create mode 100644 askama_actix/Cargo.toml create mode 100644 askama_actix/src/lib.rs create mode 100644 askama_actix/templates/hello.html create mode 100644 askama_actix/tests/basic.rs delete mode 100644 testing/tests/actix_web.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1377b3e..a6da4c6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -29,11 +29,10 @@ jobs: - uses: actions-rs/cargo@v1 with: command: build - args: --workspace --all-targets + args: --all-targets - uses: actions-rs/cargo@v1 with: command: test - args: --workspace stable-integrations: runs-on: ubuntu-latest @@ -48,6 +47,20 @@ jobs: cd testing cargo test --features full + Actix-Web: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: clippy + - run: | + cargo test --package askama_actix --all-targets + cargo clippy --package askama_actix --all-targets -- -D warnings + nightly: runs-on: ubuntu-latest steps: @@ -71,12 +84,6 @@ jobs: toolchain: stable override: true components: rustfmt, clippy - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - uses: actions-rs/cargo@v1 - if: always() - with: - command: clippy - args: --workspace --all-targets -- -D warnings + - run: | + cargo fmt --all -- --check + cargo clippy --all-targets -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index 286fd1e..379729d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,16 @@ [workspace] -members = ["askama", "askama_derive", "askama_escape", "askama_shared", "testing"] +members = [ + "askama", + "askama_actix", + "askama_derive", + "askama_escape", + "askama_shared", + "testing", +] +default-members = [ + "askama", + "askama_derive", + "askama_escape", + "askama_shared", + "testing", +] diff --git a/askama/Cargo.toml b/askama/Cargo.toml index dd20fbd..ed729f7 100644 --- a/askama/Cargo.toml +++ b/askama/Cargo.toml @@ -26,7 +26,7 @@ serde-yaml = ["askama_shared/yaml"] num-traits = ["askama_shared/num-traits"] with-iron = ["iron", "askama_derive/iron"] with-rocket = ["rocket", "askama_derive/rocket"] -with-actix-web = ["actix-web", "askama_derive/actix-web", "mime", "mime_guess", "bytes", "futures"] +with-actix-web = ["askama_derive/actix-web"] with-gotham = ["gotham", "askama_derive/gotham", "hyper", "mime", "mime_guess"] [dependencies] @@ -35,13 +35,10 @@ askama_escape = { version = "0.3.0", path = "../askama_escape" } askama_shared = { version = "0.9.0", path = "../askama_shared", default-features = false } iron = { version = ">= 0.5, < 0.7", optional = true } rocket = { version = "0.4", default-features = false, optional = true } -actix-web = { version = "2", optional = true } -futures = { version = "0.3", optional = true } mime = { version = "0.3", optional = true } mime_guess = { version = "2.0.0-alpha", optional = true } gotham = { version = "0.3", default-features = false, optional = true } hyper = { version = "0.12", optional = true } -bytes = { version = "0.5", optional = true } [package.metadata.docs.rs] features = ["config", "humansize", "num-traits", "serde-json", "serde-yaml"] diff --git a/askama/src/lib.rs b/askama/src/lib.rs index cd09a6b..7991185 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -542,65 +542,33 @@ pub mod rocket { } } -#[cfg(all(feature = "mime_guess", feature = "mime"))] -fn get_mime_type(ext: &str) -> mime_guess::Mime { - let basic_type = mime_guess::from_ext(ext).first_or_octet_stream(); - for (simple, utf_8) in &TEXT_TYPES { - if &basic_type == simple { - return utf_8.clone(); +pub mod mime { + #[cfg(all(feature = "mime_guess", feature = "mime"))] + pub fn extension_to_mime_type(ext: &str) -> mime_guess::Mime { + let basic_type = mime_guess::from_ext(ext).first_or_octet_stream(); + for (simple, utf_8) in &TEXT_TYPES { + if &basic_type == simple { + return utf_8.clone(); + } } + basic_type } - basic_type -} - -#[cfg(all(feature = "mime_guess", feature = "mime"))] -const TEXT_TYPES: [(mime_guess::Mime, mime_guess::Mime); 6] = [ - (mime::TEXT_PLAIN, mime::TEXT_PLAIN_UTF_8), - (mime::TEXT_HTML, mime::TEXT_HTML_UTF_8), - (mime::TEXT_CSS, mime::TEXT_CSS_UTF_8), - (mime::TEXT_CSV, mime::TEXT_CSV_UTF_8), - ( - mime::TEXT_TAB_SEPARATED_VALUES, - mime::TEXT_TAB_SEPARATED_VALUES_UTF_8, - ), - ( - mime::APPLICATION_JAVASCRIPT, - mime::APPLICATION_JAVASCRIPT_UTF_8, - ), -]; - -#[cfg(feature = "with-actix-web")] -pub mod actix_web { - use actix_web; - use bytes; - use mime_guess; - - use std::fmt; - - // actix_web technically has this as a pub fn in later versions, fs::file_extension_to_mime. - // Older versions that don't have it exposed are easier this way. If ext is empty or no - // associated type was found, then this returns `application/octet-stream`, in line with how - // actix_web handles it in newer releases. - pub use self::actix_web::{ - error::ErrorInternalServerError, Error, HttpRequest, HttpResponse, Responder, - }; - pub trait TemplateIntoResponse { - fn into_response(&self) -> Result; - } - - impl TemplateIntoResponse for T { - fn into_response(&self) -> Result { - let mut buffer = actix_web::web::BytesMut::with_capacity(self.size_hint()); - self.render_into(&mut buffer) - .map_err(|_| ErrorInternalServerError("Template parsing error"))?; - - let ctype = super::get_mime_type(self.extension().unwrap_or("txt")).to_string(); - Ok(HttpResponse::Ok() - .content_type(ctype.as_str()) - .body(buffer.freeze())) - } - } + #[cfg(all(feature = "mime_guess", feature = "mime"))] + const TEXT_TYPES: [(mime_guess::Mime, mime_guess::Mime); 6] = [ + (mime::TEXT_PLAIN, mime::TEXT_PLAIN_UTF_8), + (mime::TEXT_HTML, mime::TEXT_HTML_UTF_8), + (mime::TEXT_CSS, mime::TEXT_CSS_UTF_8), + (mime::TEXT_CSV, mime::TEXT_CSV_UTF_8), + ( + mime::TEXT_TAB_SEPARATED_VALUES, + mime::TEXT_TAB_SEPARATED_VALUES_UTF_8, + ), + ( + mime::APPLICATION_JAVASCRIPT, + mime::APPLICATION_JAVASCRIPT_UTF_8, + ), + ]; } #[cfg(feature = "with-gotham")] @@ -611,7 +579,7 @@ pub mod gotham { pub use hyper::{Body, Response, StatusCode}; pub fn respond(t: &T, ext: &str) -> Response { - let mime_type = super::get_mime_type(ext).to_string(); + let mime_type = super::mime::extension_to_mime_type(ext).to_string(); match t.render() { Ok(body) => Response::builder() diff --git a/askama_actix/Cargo.toml b/askama_actix/Cargo.toml new file mode 100644 index 0000000..1e857a5 --- /dev/null +++ b/askama_actix/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "askama_actix" +version = "0.9.0" +authors = ["Dirkjan Ochtman "] +description = "Actix-Web 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 = ".." +edition = "2018" + +[dependencies] +actix-web = { version = "2" } +askama = { version = "0.9.0", path = "../askama", features = ["with-actix-web", "mime", "mime_guess"] } +bytes = { version = "0.5" } +futures = { version = "0.3" } + +[dev-dependencies] +actix-rt = { version = "1", default-features = false } diff --git a/askama_actix/src/lib.rs b/askama_actix/src/lib.rs new file mode 100644 index 0000000..7372034 --- /dev/null +++ b/askama_actix/src/lib.rs @@ -0,0 +1,22 @@ +pub use askama::*; +use bytes::BytesMut; + +use actix_web::{error::ErrorInternalServerError, Error, HttpResponse}; + +pub trait TemplateIntoResponse { + fn into_response(&self) -> ::std::result::Result; +} + +impl TemplateIntoResponse for T { + fn into_response(&self) -> ::std::result::Result { + let mut buffer = BytesMut::with_capacity(self.size_hint()); + self.render_into(&mut buffer) + .map_err(|_| ErrorInternalServerError("Template parsing error"))?; + + let ctype = + askama::mime::extension_to_mime_type(self.extension().unwrap_or("txt")).to_string(); + Ok(HttpResponse::Ok() + .content_type(ctype.as_str()) + .body(buffer.freeze())) + } +} diff --git a/askama_actix/templates/hello.html b/askama_actix/templates/hello.html new file mode 100644 index 0000000..8149be7 --- /dev/null +++ b/askama_actix/templates/hello.html @@ -0,0 +1 @@ +Hello, {{ name }}! diff --git a/askama_actix/tests/basic.rs b/askama_actix/tests/basic.rs new file mode 100644 index 0000000..f4beff7 --- /dev/null +++ b/askama_actix/tests/basic.rs @@ -0,0 +1,53 @@ +use actix_web::http::header::CONTENT_TYPE; +use actix_web::test; +use actix_web::web; +use askama_actix::{Template, TemplateIntoResponse}; +use bytes::Bytes; + +#[derive(Template)] +#[template(path = "hello.html")] +struct HelloTemplate<'a> { + name: &'a str, +} + +#[actix_rt::test] +async fn test_actix_web() { + let srv = test::start(|| { + actix_web::App::new() + .service(web::resource("/").to(|| async { HelloTemplate { name: "world" } })) + }); + + let request = srv.get("/"); + let mut response = request.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response.headers().get(CONTENT_TYPE).unwrap(), + "text/html; charset=utf-8" + ); + + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static("Hello, world!".as_ref())); +} + +#[actix_rt::test] +async fn test_actix_web_responder() { + let srv = test::start(|| { + actix_web::App::new().service(web::resource("/").to(|| { + async { + let name = "world".to_owned(); + HelloTemplate { name: &name }.into_response() + } + })) + }); + + let request = srv.get("/"); + let mut response = request.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response.headers().get(CONTENT_TYPE).unwrap(), + "text/html; charset=utf-8" + ); + + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static("Hello, world!".as_ref())); +} diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 49e5dda..430afb8 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -241,15 +241,15 @@ impl<'a> Generator<'a> { // Implement Actix-web's `Responder`. fn impl_actix_web_responder(&mut self, buf: &mut Buffer) { - self.write_header(buf, "::askama::actix_web::Responder", None); - buf.writeln("type Future = ::futures::future::Ready<::std::result::Result<::askama::actix_web::HttpResponse, Self::Error>>;"); - buf.writeln("type Error = ::askama::actix_web::Error;"); + self.write_header(buf, "::actix_web::Responder", None); + buf.writeln("type Future = ::futures::future::Ready<::std::result::Result<::actix_web::HttpResponse, Self::Error>>;"); + buf.writeln("type Error = ::actix_web::Error;"); buf.writeln( - "fn respond_to(self, _req: &::askama::actix_web::HttpRequest) \ + "fn respond_to(self, _req: &::actix_web::HttpRequest) \ -> Self::Future {", ); - buf.writeln("use ::askama::actix_web::TemplateIntoResponse;"); + buf.writeln("use ::askama_actix::TemplateIntoResponse;"); buf.writeln("::futures::future::ready(self.into_response())"); buf.writeln("}"); diff --git a/testing/Cargo.toml b/testing/Cargo.toml index f093892..e7b9668 100644 --- a/testing/Cargo.toml +++ b/testing/Cargo.toml @@ -6,20 +6,15 @@ workspace = ".." edition = "2018" [features] -actix = ["actix-web", "actix-rt", "bytes", "askama/with-actix-web", "futures"] default = [] -full = ["actix", "with-iron", "serde-json", "with-gotham"] +full = ["with-iron", "serde-json", "with-gotham"] 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 = "2", optional = true } -actix-rt = { version = "1", optional = true } -futures = { version = "0.3", optional = true } askama = { path = "../askama", version = "*" } -bytes = { version = "0.5", optional = true } iron = { version = "0.6", optional = true } rocket = { version = "0.4", default-features = false, optional = true } serde_json = { version = "1.0", optional = true } diff --git a/testing/tests/actix_web.rs b/testing/tests/actix_web.rs deleted file mode 100644 index 8beef3d..0000000 --- a/testing/tests/actix_web.rs +++ /dev/null @@ -1,54 +0,0 @@ -#![cfg(feature = "actix")] -use actix_web::http::header::CONTENT_TYPE; -use actix_web::test; -use actix_web::web; -use askama::{actix_web::TemplateIntoResponse, Template}; -use bytes::Bytes; - -#[derive(Template)] -#[template(path = "hello.html")] -struct HelloTemplate<'a> { - name: &'a str, -} - -#[actix_rt::test] -async fn test_actix_web() { - let srv = test::start(|| { - actix_web::App::new() - .service(web::resource("/").to(|| async { HelloTemplate { name: "world" } })) - }); - - let request = srv.get("/"); - let mut response = request.send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response.headers().get(CONTENT_TYPE).unwrap(), - "text/html; charset=utf-8" - ); - - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static("Hello, world!".as_ref())); -} - -#[actix_rt::test] -async fn test_actix_web_responder() { - let srv = test::start(|| { - actix_web::App::new().service(web::resource("/").to(|| { - async { - let name = "world".to_owned(); - HelloTemplate { name: &name }.into_response() - } - })) - }); - - let request = srv.get("/"); - let mut response = request.send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response.headers().get(CONTENT_TYPE).unwrap(), - "text/html; charset=utf-8" - ); - - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static("Hello, world!".as_ref())); -} -- cgit