From 064077a9001e121a12fc234fee44514840380a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Tue, 24 May 2022 14:20:09 +0200 Subject: [PATCH] Move code generation into askama_derive --- askama/src/lib.rs | 9 +- askama_derive/Cargo.toml | 37 +++++--- .../src/config.rs | 0 .../src/generator.rs | 43 +++++---- .../src/heritage.rs | 0 {askama_shared => askama_derive}/src/input.rs | 3 +- askama_derive/src/lib.rs | 92 ++++++++++++++++++- .../src/parser.rs | 0 .../templates/a.html | 0 .../templates/b.html | 0 .../templates/sub/b.html | 0 .../templates/sub/c.html | 0 .../templates/sub/sub1/d.html | 0 askama_shared/src/filters/mod.rs | 36 -------- askama_shared/src/lib.rs | 51 ---------- 15 files changed, 136 insertions(+), 135 deletions(-) rename {askama_shared => askama_derive}/src/config.rs (100%) rename {askama_shared => askama_derive}/src/generator.rs (98%) rename {askama_shared => askama_derive}/src/heritage.rs (100%) rename {askama_shared => askama_derive}/src/input.rs (99%) rename {askama_shared => askama_derive}/src/parser.rs (100%) rename {askama_shared => askama_derive}/templates/a.html (100%) rename {askama_shared => askama_derive}/templates/b.html (100%) rename {askama_shared => askama_derive}/templates/sub/b.html (100%) rename {askama_shared => askama_derive}/templates/sub/c.html (100%) rename {askama_shared => askama_derive}/templates/sub/sub1/d.html (100%) diff --git a/askama/src/lib.rs b/askama/src/lib.rs index 02febf96f..b0a83ff94 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -63,19 +63,12 @@ #![deny(elided_lifetimes_in_paths)] #![deny(unreachable_pub)] -pub use askama_derive::*; +pub use askama_derive::Template; pub use askama_escape::{Html, Text}; pub use askama_shared::{ self as shared, filters, helpers, DynTemplate, Error, MarkupDisplay, Result, Template, }; -#[deprecated(since = "0.11.1", note = "The only function in this mod is deprecated")] -pub mod mime { - #[cfg(all(feature = "mime_guess", feature = "mime"))] - #[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 /// /// This function is now deprecated and does nothing. diff --git a/askama_derive/Cargo.toml b/askama_derive/Cargo.toml index 016960d5e..ce7abfa36 100644 --- a/askama_derive/Cargo.toml +++ b/askama_derive/Cargo.toml @@ -13,20 +13,27 @@ edition = "2018" proc-macro = true [features] -config = ["askama_shared/config"] -humansize = ["askama_shared/humansize"] -markdown = ["askama_shared/markdown"] -urlencode = ["askama_shared/percent-encoding"] -serde-json = ["askama_shared/json"] -serde-yaml = ["askama_shared/yaml"] -num-traits = ["askama_shared/num-traits"] -with-actix-web = ["askama_shared/actix-web"] -with-axum = ["askama_shared/axum"] -with-gotham = ["askama_shared/gotham"] -with-mendes = ["askama_shared/mendes"] -with-rocket = ["askama_shared/rocket"] -with-tide = ["askama_shared/tide"] -with-warp = ["askama_shared/warp"] +config = ["serde", "toml"] +humansize = [] +markdown = [] +urlencode = [] +serde-json = [] +serde-yaml = [] +num-traits = [] +with-actix-web = [] +with-axum = [] +with-gotham = [] +with-mendes = [] +with-rocket = [] +with-tide = [] +with-warp = [] [dependencies] -askama_shared = { version = "0.13.0", path = "../askama_shared", default-features = false } +mime = "0.3" +mime_guess = "2" +nom = "7" +proc-macro2 = "1" +quote = "1" +serde = { version = "1.0", optional = true, features = ["derive"] } +syn = "1" +toml = { version = "0.5", optional = true } diff --git a/askama_shared/src/config.rs b/askama_derive/src/config.rs similarity index 100% rename from askama_shared/src/config.rs rename to askama_derive/src/config.rs diff --git a/askama_shared/src/generator.rs b/askama_derive/src/generator.rs similarity index 98% rename from askama_shared/src/generator.rs rename to askama_derive/src/generator.rs index ea95bd343..39a538076 100644 --- a/askama_shared/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -2,9 +2,9 @@ use crate::config::{get_template_source, read_config_file, Config, WhitespaceHan use crate::heritage::{Context, Heritage}; use crate::input::{Print, Source, TemplateInput}; use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Whitespace, Ws}; -use crate::{filters, CompileError}; +use crate::CompileError; -use proc_macro2::TokenStream; +use proc_macro::TokenStream; use quote::{quote, ToTokens}; use std::collections::HashMap; @@ -12,9 +12,8 @@ use std::path::{Path, PathBuf}; use std::{cmp, hash, mem, str}; /// The actual implementation for askama_derive::Template -#[doc(hidden)] -pub fn derive_template(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse2(input).unwrap(); +pub(crate) fn derive_template(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); match build_template(&ast) { Ok(source) => source.parse().unwrap(), Err(e) => e.into_compile_error(), @@ -302,19 +301,19 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { self.impl_template(ctx, &mut buf)?; self.impl_display(&mut buf)?; - #[cfg(feature = "actix-web")] + #[cfg(feature = "with-actix-web")] self.impl_actix_web_responder(&mut buf)?; - #[cfg(feature = "axum")] + #[cfg(feature = "with-axum")] self.impl_axum_into_response(&mut buf)?; - #[cfg(feature = "gotham")] + #[cfg(feature = "with-gotham")] self.impl_gotham_into_response(&mut buf)?; - #[cfg(feature = "mendes")] + #[cfg(feature = "with-mendes")] self.impl_mendes_responder(&mut buf)?; - #[cfg(feature = "rocket")] + #[cfg(feature = "with-rocket")] self.impl_rocket_responder(&mut buf)?; - #[cfg(feature = "tide")] + #[cfg(feature = "with-tide")] self.impl_tide_integrations(&mut buf)?; - #[cfg(feature = "warp")] + #[cfg(feature = "with-warp")] self.impl_warp_reply(&mut buf)?; Ok(buf.buf) @@ -405,7 +404,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } // Implement Actix-web's `Responder`. - #[cfg(feature = "actix-web")] + #[cfg(feature = "with-actix-web")] fn impl_actix_web_responder(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { self.write_header(buf, "::askama_actix::actix_web::Responder", None)?; buf.writeln("type Body = ::askama_actix::actix_web::body::BoxBody;")?; @@ -420,7 +419,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } // Implement Axum's `IntoResponse`. - #[cfg(feature = "axum")] + #[cfg(feature = "with-axum")] fn impl_axum_into_response(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { self.write_header(buf, "::askama_axum::IntoResponse", None)?; buf.writeln("#[inline]")?; @@ -435,7 +434,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } // Implement gotham's `IntoResponse`. - #[cfg(feature = "gotham")] + #[cfg(feature = "with-gotham")] fn impl_gotham_into_response(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { self.write_header(buf, "::askama_gotham::IntoResponse", None)?; buf.writeln("#[inline]")?; @@ -450,7 +449,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } // Implement mendes' `Responder`. - #[cfg(feature = "mendes")] + #[cfg(feature = "with-mendes")] fn impl_mendes_responder(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { let param = syn::parse_str("A: ::mendes::Application").unwrap(); @@ -500,7 +499,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } // Implement Rocket's `Responder`. - #[cfg(feature = "rocket")] + #[cfg(feature = "with-rocket")] fn impl_rocket_responder(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { let lifetime = syn::Lifetime::new("'askama", proc_macro2::Span::call_site()); let param = syn::GenericParam::Lifetime(syn::LifetimeDef::new(lifetime)); @@ -523,7 +522,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { Ok(()) } - #[cfg(feature = "tide")] + #[cfg(feature = "with-tide")] fn impl_tide_integrations(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { let ext = self.input.extension().unwrap_or("txt"); @@ -549,7 +548,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { buf.writeln("}\n}") } - #[cfg(feature = "warp")] + #[cfg(feature = "with-warp")] fn impl_warp_reply(&mut self, buf: &mut Buffer) -> Result<(), CompileError> { self.write_header(buf, "::askama_warp::warp::reply::Reply", None)?; buf.writeln("#[inline]")?; @@ -1381,11 +1380,11 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { name = "json"; } - #[cfg(not(feature = "json"))] + #[cfg(not(feature = "serde-json"))] if name == "json" { return Err("the `json` filter requires the `serde-json` feature to be enabled".into()); } - #[cfg(not(feature = "yaml"))] + #[cfg(not(feature = "serde-yaml"))] if name == "yaml" { return Err("the `yaml` filter requires the `serde-yaml` feature to be enabled".into()); } @@ -1396,7 +1395,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { "::askama::filters::{}({}, ", name, self.input.escaper )); - } else if filters::BUILT_IN_FILTERS.contains(&name) { + } else if crate::BUILT_IN_FILTERS.contains(&name) { buf.write(&format!("::askama::filters::{}(", name)); } else { buf.write(&format!("filters::{}(", name)); diff --git a/askama_shared/src/heritage.rs b/askama_derive/src/heritage.rs similarity index 100% rename from askama_shared/src/heritage.rs rename to askama_derive/src/heritage.rs diff --git a/askama_shared/src/input.rs b/askama_derive/src/input.rs similarity index 99% rename from askama_shared/src/input.rs rename to askama_derive/src/input.rs index 1f367fd4f..c09f3d03b 100644 --- a/askama_shared/src/input.rs +++ b/askama_derive/src/input.rs @@ -181,8 +181,7 @@ impl Default for Print { } } -#[doc(hidden)] -pub fn extension_to_mime_type(ext: &str) -> Mime { +pub(crate) 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 { diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs index b41fa6ac5..2acf58380 100644 --- a/askama_derive/src/lib.rs +++ b/askama_derive/src/lib.rs @@ -2,9 +2,99 @@ #![deny(elided_lifetimes_in_paths)] #![deny(unreachable_pub)] +use std::borrow::Cow; +use std::fmt; + use proc_macro::TokenStream; +use proc_macro2::Span; + +mod config; +mod generator; +mod heritage; +mod input; +mod parser; #[proc_macro_derive(Template, attributes(template))] pub fn derive_template(input: TokenStream) -> TokenStream { - askama_shared::derive_template(input.into()).into() + generator::derive_template(input) +} + +#[derive(Debug, Clone)] +struct CompileError { + msg: Cow<'static, str>, + span: Span, } + +impl CompileError { + fn new>>(s: S, span: Span) -> Self { + Self { + msg: s.into(), + span, + } + } + + fn into_compile_error(self) -> TokenStream { + syn::Error::new(self.span, self.msg) + .to_compile_error() + .into() + } +} + +impl std::error::Error for CompileError {} + +impl fmt::Display for CompileError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(&self.msg) + } +} + +impl From<&'static str> for CompileError { + #[inline] + fn from(s: &'static str) -> Self { + Self::new(s, Span::call_site()) + } +} + +impl From for CompileError { + #[inline] + fn from(s: String) -> Self { + Self::new(s, Span::call_site()) + } +} + +// This is used by the code generator to decide whether a named filter is part of +// Askama or should refer to a local `filters` module. It should contain all the +// filters shipped with Askama, even the optional ones (since optional inclusion +// in the const vector based on features seems impossible right now). +const BUILT_IN_FILTERS: &[&str] = &[ + "abs", + "capitalize", + "center", + "e", + "escape", + "filesizeformat", + "fmt", + "format", + "indent", + "into_f64", + "into_isize", + "join", + "linebreaks", + "linebreaksbr", + "paragraphbreaks", + "lower", + "lowercase", + "safe", + "trim", + "truncate", + "upper", + "uppercase", + "urlencode", + "urlencode_strict", + "wordcount", + // optional features, reserve the names anyway: + "json", + "markdown", + "yaml", +]; diff --git a/askama_shared/src/parser.rs b/askama_derive/src/parser.rs similarity index 100% rename from askama_shared/src/parser.rs rename to askama_derive/src/parser.rs diff --git a/askama_shared/templates/a.html b/askama_derive/templates/a.html similarity index 100% rename from askama_shared/templates/a.html rename to askama_derive/templates/a.html diff --git a/askama_shared/templates/b.html b/askama_derive/templates/b.html similarity index 100% rename from askama_shared/templates/b.html rename to askama_derive/templates/b.html diff --git a/askama_shared/templates/sub/b.html b/askama_derive/templates/sub/b.html similarity index 100% rename from askama_shared/templates/sub/b.html rename to askama_derive/templates/sub/b.html diff --git a/askama_shared/templates/sub/c.html b/askama_derive/templates/sub/c.html similarity index 100% rename from askama_shared/templates/sub/c.html rename to askama_derive/templates/sub/c.html diff --git a/askama_shared/templates/sub/sub1/d.html b/askama_derive/templates/sub/sub1/d.html similarity index 100% rename from askama_shared/templates/sub/sub1/d.html rename to askama_derive/templates/sub/sub1/d.html diff --git a/askama_shared/src/filters/mod.rs b/askama_shared/src/filters/mod.rs index 6437ce0e3..17826028f 100644 --- a/askama_shared/src/filters/mod.rs +++ b/askama_shared/src/filters/mod.rs @@ -43,42 +43,6 @@ const URLENCODE_STRICT_SET: &AsciiSet = &NON_ALPHANUMERIC // Same as URLENCODE_STRICT_SET, but preserves forward slashes for encoding paths const URLENCODE_SET: &AsciiSet = &URLENCODE_STRICT_SET.remove(b'/'); -// This is used by the code generator to decide whether a named filter is part of -// Askama or should refer to a local `filters` module. It should contain all the -// filters shipped with Askama, even the optional ones (since optional inclusion -// in the const vector based on features seems impossible right now). -pub const BUILT_IN_FILTERS: &[&str] = &[ - "abs", - "capitalize", - "center", - "e", - "escape", - "filesizeformat", - "fmt", - "format", - "indent", - "into_f64", - "into_isize", - "join", - "linebreaks", - "linebreaksbr", - "paragraphbreaks", - "lower", - "lowercase", - "safe", - "trim", - "truncate", - "upper", - "uppercase", - "urlencode", - "urlencode_strict", - "wordcount", - // optional features, reserve the names anyway: - "json", - "markdown", - "yaml", -]; - /// Marks a string (or other `Display` type) as safe /// /// Use this is you want to allow markup in an expression, or if you know diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index a3ee4c138..cb2640681 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -3,23 +3,14 @@ #![deny(elided_lifetimes_in_paths)] #![deny(unreachable_pub)] -use std::borrow::Cow; use std::fmt; -pub use crate::generator::derive_template; -pub use crate::input::extension_to_mime_type; pub use askama_escape::MarkupDisplay; -use proc_macro2::{Span, TokenStream}; -mod config; mod error; pub use crate::error::{Error, Result}; pub mod filters; -mod generator; pub mod helpers; -mod heritage; -mod input; -mod parser; /// Main `Template` trait; implementations are generally derived /// @@ -107,48 +98,6 @@ impl fmt::Display for dyn DynTemplate { } } -#[derive(Debug, Clone)] -struct CompileError { - msg: Cow<'static, str>, - span: Span, -} - -impl CompileError { - fn new>>(s: S, span: Span) -> Self { - Self { - msg: s.into(), - span, - } - } - - fn into_compile_error(self) -> TokenStream { - syn::Error::new(self.span, self.msg).to_compile_error() - } -} - -impl std::error::Error for CompileError {} - -impl fmt::Display for CompileError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(&self.msg) - } -} - -impl From<&'static str> for CompileError { - #[inline] - fn from(s: &'static str) -> Self { - Self::new(s, Span::call_site()) - } -} - -impl From for CompileError { - #[inline] - fn from(s: String) -> Self { - Self::new(s, Span::call_site()) - } -} - #[cfg(test)] #[allow(clippy::blacklisted_name)] mod tests {