diff --git a/askama_shared/src/derive.rs b/askama_shared/src/derive.rs deleted file mode 100644 index b8f0a2f12..000000000 --- a/askama_shared/src/derive.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::collections::HashMap; -use std::path::PathBuf; - -use proc_macro2::TokenStream; - -use crate::heritage::{Context, Heritage}; -use crate::input::{Print, Source, TemplateInput}; -use crate::parser::{parse, Expr, Node}; -use crate::{generator, get_template_source, read_config_file, CompileError, Config}; - -/// The actual implementation for askama_derive::Template -#[doc(hidden)] -pub fn derive_template(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse2(input).unwrap(); - match build_template(&ast) { - Ok(source) => source.parse().unwrap(), - Err(e) => e.into_compile_error(), - } -} - -/// Takes a `syn::DeriveInput` and generates source code for it -/// -/// Reads the metadata from the `template()` attribute to get the template -/// metadata, then fetches the source from the filesystem. The source is -/// parsed, and the parse tree is fed to the code generator. Will print -/// the parse tree and/or generated source according to the `print` key's -/// value as passed to the `template()` attribute. -fn build_template(ast: &syn::DeriveInput) -> Result { - let config_toml = read_config_file()?; - let config = Config::new(&config_toml)?; - let input = TemplateInput::new(ast, &config)?; - let source: String = match input.source { - Source::Source(ref s) => s.clone(), - Source::Path(_) => get_template_source(&input.path)?, - }; - - let mut sources = HashMap::new(); - find_used_templates(&input, &mut sources, source)?; - - let mut parsed = HashMap::new(); - for (path, src) in &sources { - parsed.insert(path.as_path(), parse(src, input.syntax)?); - } - - let mut contexts = HashMap::new(); - for (path, nodes) in &parsed { - contexts.insert(*path, Context::new(input.config, path, nodes)?); - } - - let ctx = &contexts[input.path.as_path()]; - let heritage = if !ctx.blocks.is_empty() || ctx.extends.is_some() { - Some(Heritage::new(ctx, &contexts)) - } else { - None - }; - - if input.print == Print::Ast || input.print == Print::All { - eprintln!("{:?}", parsed[input.path.as_path()]); - } - - let code = generator::generate(&input, &contexts, heritage.as_ref())?; - if input.print == Print::Code || input.print == Print::All { - eprintln!("{}", code); - } - Ok(code) -} - -fn find_used_templates( - input: &TemplateInput<'_>, - map: &mut HashMap, - source: String, -) -> Result<(), CompileError> { - let mut dependency_graph = Vec::new(); - let mut check = vec![(input.path.clone(), source)]; - while let Some((path, source)) = check.pop() { - for n in parse(&source, input.syntax)? { - match n { - Node::Extends(Expr::StrLit(extends)) => { - let extends = input.config.find_template(extends, Some(&path))?; - let dependency_path = (path.clone(), extends.clone()); - if dependency_graph.contains(&dependency_path) { - return Err(format!( - "cyclic dependecy in graph {:#?}", - dependency_graph - .iter() - .map(|e| format!("{:#?} --> {:#?}", e.0, e.1)) - .collect::>() - ) - .into()); - } - dependency_graph.push(dependency_path); - let source = get_template_source(&extends)?; - check.push((extends, source)); - } - Node::Import(_, import, _) => { - let import = input.config.find_template(import, Some(&path))?; - let source = get_template_source(&import)?; - check.push((import, source)); - } - _ => {} - } - } - map.insert(path, source); - } - Ok(()) -} diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index f7783afc0..c9e482db4 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -1,24 +1,112 @@ -use super::{get_template_source, CompileError}; -use crate::filters; use crate::heritage::{Context, Heritage}; -use crate::input::{Source, TemplateInput}; +use crate::input::{Print, Source, TemplateInput}; use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Ws}; +use crate::{filters, get_template_source, read_config_file, CompileError, Config}; +use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use std::collections::HashMap; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{cmp, hash, mem, str}; -pub(crate) fn generate( - input: &TemplateInput<'_>, - contexts: &HashMap<&Path, Context<'_>, S>, - heritage: Option<&Heritage<'_>>, -) -> Result { - Generator::new(input, contexts, heritage, MapChain::new()) - .build(&contexts[input.path.as_path()]) +/// The actual implementation for askama_derive::Template +#[doc(hidden)] +pub fn derive_template(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse2(input).unwrap(); + match build_template(&ast) { + Ok(source) => source.parse().unwrap(), + Err(e) => e.into_compile_error(), + } +} + +/// Takes a `syn::DeriveInput` and generates source code for it +/// +/// Reads the metadata from the `template()` attribute to get the template +/// metadata, then fetches the source from the filesystem. The source is +/// parsed, and the parse tree is fed to the code generator. Will print +/// the parse tree and/or generated source according to the `print` key's +/// value as passed to the `template()` attribute. +fn build_template(ast: &syn::DeriveInput) -> Result { + let config_toml = read_config_file()?; + let config = Config::new(&config_toml)?; + let input = TemplateInput::new(ast, &config)?; + let source: String = match input.source { + Source::Source(ref s) => s.clone(), + Source::Path(_) => get_template_source(&input.path)?, + }; + + let mut sources = HashMap::new(); + find_used_templates(&input, &mut sources, source)?; + + let mut parsed = HashMap::new(); + for (path, src) in &sources { + parsed.insert(path.as_path(), parse(src, input.syntax)?); + } + + let mut contexts = HashMap::new(); + for (path, nodes) in &parsed { + contexts.insert(*path, Context::new(input.config, path, nodes)?); + } + + let ctx = &contexts[input.path.as_path()]; + let heritage = if !ctx.blocks.is_empty() || ctx.extends.is_some() { + Some(Heritage::new(ctx, &contexts)) + } else { + None + }; + + if input.print == Print::Ast || input.print == Print::All { + eprintln!("{:?}", parsed[input.path.as_path()]); + } + + let code = Generator::new(&input, &contexts, heritage.as_ref(), MapChain::new()) + .build(&contexts[input.path.as_path()])?; + if input.print == Print::Code || input.print == Print::All { + eprintln!("{}", code); + } + Ok(code) } +fn find_used_templates( + input: &TemplateInput<'_>, + map: &mut HashMap, + source: String, +) -> Result<(), CompileError> { + let mut dependency_graph = Vec::new(); + let mut check = vec![(input.path.clone(), source)]; + while let Some((path, source)) = check.pop() { + for n in parse(&source, input.syntax)? { + match n { + Node::Extends(Expr::StrLit(extends)) => { + let extends = input.config.find_template(extends, Some(&path))?; + let dependency_path = (path.clone(), extends.clone()); + if dependency_graph.contains(&dependency_path) { + return Err(format!( + "cyclic dependecy in graph {:#?}", + dependency_graph + .iter() + .map(|e| format!("{:#?} --> {:#?}", e.0, e.1)) + .collect::>() + ) + .into()); + } + dependency_graph.push(dependency_path); + let source = get_template_source(&extends)?; + check.push((extends, source)); + } + Node::Import(_, import, _) => { + let import = input.config.find_template(import, Some(&path))?; + let source = get_template_source(&import)?; + check.push((import, source)); + } + _ => {} + } + } + map.insert(path, source); + } + Ok(()) +} struct Generator<'a, S: std::hash::BuildHasher> { // The template input state: original struct AST and attributes input: &'a TemplateInput<'a>, diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index 61ddb0084..81500e12f 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -13,11 +13,10 @@ use proc_macro2::{Span, TokenStream}; #[cfg(feature = "serde")] use serde::Deserialize; -pub use crate::derive::derive_template; +pub use crate::generator::derive_template; pub use crate::input::extension_to_mime_type; pub use askama_escape::MarkupDisplay; -mod derive; mod error; pub use crate::error::{Error, Result}; pub mod filters;