Skip to content

Commit

Permalink
Move code generation into askama_derive
Browse files Browse the repository at this point in the history
  • Loading branch information
Kijewski authored and djc committed May 24, 2022
1 parent e30cad3 commit 064077a
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 135 deletions.
9 changes: 1 addition & 8 deletions askama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
37 changes: 22 additions & 15 deletions askama_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
File renamed without changes.
43 changes: 21 additions & 22 deletions askama_shared/src/generator.rs → askama_derive/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ 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;
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(),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;")?;
Expand All @@ -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]")?;
Expand All @@ -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]")?;
Expand All @@ -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();

Expand Down Expand Up @@ -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));
Expand All @@ -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");

Expand All @@ -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]")?;
Expand Down Expand Up @@ -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());
}
Expand All @@ -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));
Expand Down
File renamed without changes.
3 changes: 1 addition & 2 deletions askama_shared/src/input.rs → askama_derive/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
92 changes: 91 additions & 1 deletion askama_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: Into<Cow<'static, str>>>(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<String> 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",
];
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
36 changes: 0 additions & 36 deletions askama_shared/src/filters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 064077a

Please sign in to comment.