From d8f75eb93d8304557da6c648002b2f2892df760c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Fri, 7 Jan 2022 00:53:54 +0100 Subject: [PATCH 1/5] Move extension_to_mime_type() to askama_shared --- askama/Cargo.toml | 6 ++++-- askama/src/lib.rs | 28 +++------------------------- askama_shared/Cargo.toml | 2 ++ askama_shared/src/input.rs | 27 +++++++++++++++++++++++++++ askama_shared/src/lib.rs | 1 + 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/askama/Cargo.toml b/askama/Cargo.toml index b3876e2b2..076f7e98a 100644 --- a/askama/Cargo.toml +++ b/askama/Cargo.toml @@ -31,12 +31,14 @@ with-rocket = ["askama_derive/rocket"] with-tide = ["askama_derive/tide"] with-warp = ["askama_derive/warp"] +# deprecated +mime = [] +mime_guess = [] + [dependencies] askama_derive = { version = "0.11.0", path = "../askama_derive" } askama_escape = { version = "0.10", path = "../askama_escape" } askama_shared = { version = "0.12.0", path = "../askama_shared", default-features = false } -mime = { version = "0.3", optional = true } -mime_guess = { version = "2", 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 3c7f072d1..f1aa91c77 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -128,33 +128,11 @@ pub use crate::shared::helpers; pub use crate::shared::{read_config_file, Error, MarkupDisplay, Result}; pub use askama_derive::*; +#[deprecated(since = "0.11.1", note = "The only function in this mod is deprecated")] 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 - } - - #[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, - ), - ]; + #[deprecated(since = "0.11.1", note = "Use Template::MIME_TYPE instead")] + pub use crate::shared::extension_to_mime_type; } /// Old build script helper to rebuild crates if contained templates have changed diff --git a/askama_shared/Cargo.toml b/askama_shared/Cargo.toml index 7f476b140..be00ae477 100644 --- a/askama_shared/Cargo.toml +++ b/askama_shared/Cargo.toml @@ -18,6 +18,8 @@ yaml = ["serde", "serde_yaml"] [dependencies] askama_escape = { version = "0.10.2", path = "../askama_escape" } humansize = { version = "1.1.0", optional = true } +mime = "0.3" +mime_guess = "2" nom = "7" num-traits = { version = "0.2.6", optional = true } proc-macro2 = "1" diff --git a/askama_shared/src/input.rs b/askama_shared/src/input.rs index 03c62d013..7d28a7eb1 100644 --- a/askama_shared/src/input.rs +++ b/askama_shared/src/input.rs @@ -3,6 +3,7 @@ use crate::{CompileError, Config, Syntax}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use mime::Mime; use quote::ToTokens; pub struct TemplateInput<'a> { @@ -239,6 +240,32 @@ impl FromStr for Print { } } +#[doc(hidden)] +pub fn extension_to_mime_type(ext: &str) -> 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 +} + +const TEXT_TYPES: [(Mime, 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(test)] mod tests { use super::*; diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index f6b94ddd4..911367a0e 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -11,6 +11,7 @@ use std::{env, fmt, fs}; #[cfg(feature = "serde")] use serde::Deserialize; +pub use crate::input::extension_to_mime_type; pub use askama_escape::MarkupDisplay; mod error; From 04de3abb682d7da0f69c3346171ca3873f2cd863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Fri, 7 Jan 2022 01:06:14 +0100 Subject: [PATCH 2/5] Unshadow function escaping() --- askama_shared/src/input.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/askama_shared/src/input.rs b/askama_shared/src/input.rs index 7d28a7eb1..25d61e1b6 100644 --- a/askama_shared/src/input.rs +++ b/askama_shared/src/input.rs @@ -161,7 +161,7 @@ impl TemplateInput<'_> { // Match extension against defined output formats - let extension = escaping.unwrap_or_else(|| { + let escaping = escaping.unwrap_or_else(|| { path.extension() .map(|s| s.to_str().unwrap()) .unwrap_or("") @@ -170,14 +170,14 @@ impl TemplateInput<'_> { let mut escaper = None; for (extensions, path) in &config.escapers { - if extensions.contains(&extension) { + if extensions.contains(&escaping) { escaper = Some(path); break; } } let escaper = escaper.ok_or_else(|| { - CompileError::String(format!("no escaper defined for extension '{}'", extension,)) + CompileError::String(format!("no escaper defined for extension '{}'", escaping,)) })?; Ok(TemplateInput { From d564edbc186e1077b79d447218eb0675dd9307bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Fri, 7 Jan 2022 01:15:40 +0100 Subject: [PATCH 3/5] Make TemplateInput::extension() reusable --- askama_shared/src/input.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/askama_shared/src/input.rs b/askama_shared/src/input.rs index 25d61e1b6..a67946170 100644 --- a/askama_shared/src/input.rs +++ b/askama_shared/src/input.rs @@ -193,11 +193,17 @@ impl TemplateInput<'_> { }) } + #[inline] pub fn extension(&self) -> Option<&str> { - self.ext.as_deref().or_else(|| extension(&self.path)) + ext_default_to_path(self.ext.as_deref(), &self.path) } } +#[inline] +pub fn ext_default_to_path<'a>(ext: Option<&'a str>, path: &'a Path) -> Option<&'a str> { + ext.or_else(|| extension(path)) +} + fn extension(path: &Path) -> Option<&str> { let ext = path.extension().map(|s| s.to_str().unwrap())?; From 858ae2c324be430dacb24e4ae7df93aa200d3be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Fri, 7 Jan 2022 01:06:35 +0100 Subject: [PATCH 4/5] Determine Content-Type during compilation --- askama/src/lib.rs | 12 ++++++++++++ askama_shared/src/generator.rs | 4 ++++ askama_shared/src/input.rs | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/askama/src/lib.rs b/askama/src/lib.rs index f1aa91c77..ccfddc8fb 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -86,6 +86,9 @@ pub trait Template { /// Provides a conservative estimate of the expanded length of the rendered template const SIZE_HINT: usize; + + /// The MIME type (Content-Type) of the data that gets rendered by this Template + const MIME_TYPE: &'static str; } /// Object-safe wrapper trait around [`Template`] implementers @@ -103,6 +106,9 @@ pub trait DynTemplate { /// Provides a conservative estimate of the expanded length of the rendered template fn size_hint(&self) -> usize; + + /// The MIME type (Content-Type) of the data that gets rendered by this Template + fn mime_type(&self) -> &'static str; } impl DynTemplate for T { @@ -121,6 +127,10 @@ impl DynTemplate for T { fn size_hint(&self) -> usize { Self::SIZE_HINT } + + fn mime_type(&self) -> &'static str { + Self::MIME_TYPE + } } pub use crate::shared::filters; @@ -162,6 +172,8 @@ mod tests { const EXTENSION: Option<&'static str> = Some("txt"); const SIZE_HINT: usize = 4; + + const MIME_TYPE: &'static str = "text/plain; charset=utf-8"; } fn render(t: &dyn DynTemplate) -> String { diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 5c6303c7a..cd18819b3 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -165,6 +165,10 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { buf.writeln(&format!("{}", size_hint))?; buf.writeln(";")?; + buf.writeln("const MIME_TYPE: &'static ::std::primitive::str = ")?; + buf.writeln(&format!("{:?}", &self.input.mime_type))?; + buf.writeln(";")?; + buf.writeln("}")?; Ok(()) } diff --git a/askama_shared/src/input.rs b/askama_shared/src/input.rs index a67946170..17410ed73 100644 --- a/askama_shared/src/input.rs +++ b/askama_shared/src/input.rs @@ -14,6 +14,7 @@ pub struct TemplateInput<'a> { pub print: Print, pub escaper: &'a str, pub ext: Option, + pub mime_type: String, pub parent: Option<&'a syn::Type>, pub path: PathBuf, } @@ -180,6 +181,10 @@ impl TemplateInput<'_> { CompileError::String(format!("no escaper defined for extension '{}'", escaping,)) })?; + let mime_type = + extension_to_mime_type(ext_default_to_path(ext.as_deref(), &path).unwrap_or("txt")) + .to_string(); + Ok(TemplateInput { ast, config, @@ -188,6 +193,7 @@ impl TemplateInput<'_> { print, escaper, ext, + mime_type, parent, path, }) From 2710792e6e4ba909b11890b773a96b87a819b33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Thu, 6 Jan 2022 14:30:46 +0100 Subject: [PATCH 5/5] Use Template::MIME_TYPE instead of extension --- askama_actix/src/lib.rs | 11 ++++------- askama_axum/src/lib.rs | 6 +++--- askama_gotham/src/lib.rs | 6 +++--- askama_mendes/src/lib.rs | 19 ++++++------------- askama_mendes/tests/basic.rs | 2 +- askama_rocket/src/lib.rs | 7 +++---- askama_tide/src/lib.rs | 10 +++------- askama_warp/src/lib.rs | 7 ++----- 8 files changed, 25 insertions(+), 43 deletions(-) diff --git a/askama_actix/src/lib.rs b/askama_actix/src/lib.rs index c4317c427..59957f4be 100644 --- a/askama_actix/src/lib.rs +++ b/askama_actix/src/lib.rs @@ -5,9 +5,9 @@ use std::fmt; use actix_web::body::BoxBody; +use actix_web::http::header::HeaderValue; use actix_web::http::StatusCode; use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError}; -use askama::mime::extension_to_mime_type; pub use askama::*; /// Newtype to let askama::Error implement actix_web::ResponseError. @@ -36,12 +36,9 @@ pub trait TemplateToResponse { impl TemplateToResponse for T { fn to_response(&self) -> HttpResponse { match self.render() { - Ok(buffer) => { - let ctype = extension_to_mime_type(T::EXTENSION.unwrap_or("txt")); - HttpResponseBuilder::new(StatusCode::OK) - .content_type(ctype) - .body(buffer) - } + Ok(buffer) => HttpResponseBuilder::new(StatusCode::OK) + .content_type(HeaderValue::from_static(T::MIME_TYPE)) + .body(buffer), Err(err) => HttpResponse::from_error(ActixError(err)), } } diff --git a/askama_axum/src/lib.rs b/askama_axum/src/lib.rs index 21217ff29..104867499 100644 --- a/askama_axum/src/lib.rs +++ b/askama_axum/src/lib.rs @@ -10,13 +10,13 @@ pub use http::Response; use http::StatusCode; use http_body::{Empty, Full}; -pub fn into_response(t: &T, ext: &str) -> Response { +pub fn into_response(t: &T, _ext: &str) -> Response { match t.render() { Ok(body) => Response::builder() .status(StatusCode::OK) .header( - "content-type", - askama::mime::extension_to_mime_type(ext).to_string(), + http::header::CONTENT_TYPE, + http::HeaderValue::from_static(T::MIME_TYPE), ) .body(body::boxed(Full::from(body))) .unwrap(), diff --git a/askama_gotham/src/lib.rs b/askama_gotham/src/lib.rs index 1241c9c06..c2c933da4 100644 --- a/askama_gotham/src/lib.rs +++ b/askama_gotham/src/lib.rs @@ -8,13 +8,13 @@ pub use gotham::handler::IntoResponse; pub use gotham::state::State; pub use hyper::{Body, Response, StatusCode}; -pub fn respond(t: &T, ext: &str) -> Response { +pub fn respond(t: &T, _ext: &str) -> Response { match t.render() { Ok(body) => Response::builder() .status(StatusCode::OK) .header( - "content-type", - mime::extension_to_mime_type(ext).to_string(), + hyper::header::CONTENT_TYPE, + hyper::header::HeaderValue::from_static(T::MIME_TYPE), ) .body(body.into()) .unwrap(), diff --git a/askama_mendes/src/lib.rs b/askama_mendes/src/lib.rs index baaa49fd5..d1ccb2c5a 100644 --- a/askama_mendes/src/lib.rs +++ b/askama_mendes/src/lib.rs @@ -2,13 +2,10 @@ #![deny(elided_lifetimes_in_paths)] #![deny(unreachable_pub)] -use std::convert::TryFrom; - use mendes::application::{Application, Responder}; use mendes::http::header::{HeaderValue, CONTENT_LENGTH, CONTENT_TYPE}; use mendes::http::request::Parts; use mendes::http::Response; -use mime_guess::MimeGuess; pub use askama::*; @@ -16,7 +13,7 @@ pub fn into_response( app: &A, req: &Parts, t: &T, - ext: Option<&str>, + _ext: Option<&str>, ) -> Response where A: Application, @@ -29,13 +26,9 @@ where Err(e) => return >::from(e).into_response(app, req), }; - let mut builder = Response::builder(); - builder = builder.header(CONTENT_LENGTH, content.len()); - if let Some(ext) = ext { - if let Some(ty) = MimeGuess::from_ext(ext).first() { - builder = builder.header(CONTENT_TYPE, HeaderValue::try_from(ty.as_ref()).unwrap()); - } - } - - builder.body(content.into()).unwrap() + Response::builder() + .header(CONTENT_LENGTH, content.len()) + .header(CONTENT_TYPE, HeaderValue::from_static(T::MIME_TYPE)) + .body(content.into()) + .unwrap() } diff --git a/askama_mendes/tests/basic.rs b/askama_mendes/tests/basic.rs index 2a07c1380..e2f5b2525 100644 --- a/askama_mendes/tests/basic.rs +++ b/askama_mendes/tests/basic.rs @@ -19,7 +19,7 @@ async fn test() { rsp.headers .get("content-type") .and_then(|hv| hv.to_str().ok()), - Some("text/plain") + Some("text/plain; charset=utf-8") ); assert_eq!(to_bytes(body).await.unwrap(), &b"Hello, world!"[..]); } diff --git a/askama_rocket/src/lib.rs b/askama_rocket/src/lib.rs index 33bd7792d..f10ba871d 100644 --- a/askama_rocket/src/lib.rs +++ b/askama_rocket/src/lib.rs @@ -5,16 +5,15 @@ use std::io::Cursor; pub use askama::*; -use rocket::http::{ContentType, Status}; +use rocket::http::{Header, Status}; pub use rocket::request::Request; use rocket::response::Response; pub use rocket::response::{Responder, Result}; -pub fn respond(t: &T, ext: &str) -> Result<'static> { +pub fn respond(t: &T, _ext: &str) -> Result<'static> { let rsp = t.render().map_err(|_| Status::InternalServerError)?; - let ctype = ContentType::from_extension(ext).ok_or(Status::InternalServerError)?; Response::build() - .header(ctype) + .header(Header::new("content-type", T::MIME_TYPE)) .sized_body(Cursor::new(rsp)) .ok() } diff --git a/askama_tide/src/lib.rs b/askama_tide/src/lib.rs index 2854cb6b6..be597bba1 100644 --- a/askama_tide/src/lib.rs +++ b/askama_tide/src/lib.rs @@ -6,16 +6,12 @@ pub use askama; pub use tide; use askama::*; -use tide::{http::Mime, Body, Response}; +use tide::{Body, Response}; -pub fn try_into_body(t: &T, ext: &str) -> Result { +pub fn try_into_body(t: &T, _ext: &str) -> Result { let string = t.render()?; let mut body = Body::from_string(string); - - if let Some(mime) = Mime::from_extension(ext) { - body.set_mime(mime); - } - + body.set_mime(T::MIME_TYPE); Ok(body) } diff --git a/askama_warp/src/lib.rs b/askama_warp/src/lib.rs index 4220efcad..b2f038744 100644 --- a/askama_warp/src/lib.rs +++ b/askama_warp/src/lib.rs @@ -9,14 +9,11 @@ use warp::http::{self, header, StatusCode}; use warp::hyper::Body; use warp::reply::Response; -pub fn reply(t: &T, ext: &str) -> Response { +pub fn reply(t: &T, _ext: &str) -> Response { match t.render() { Ok(body) => http::Response::builder() .status(StatusCode::OK) - .header( - header::CONTENT_TYPE, - mime::extension_to_mime_type(ext).to_string(), - ) + .header(header::CONTENT_TYPE, T::MIME_TYPE) .body(body.into()), Err(_) => http::Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR)