Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 45 additions & 15 deletions askama_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use askama_shared::heritage::{Context, Heritage};
use askama_shared::input::{Print, Source, TemplateInput};
use askama_shared::parser::{parse, Expr, Node};
use askama_shared::{
generator, get_template_source, read_config_file, CompileError, Config, Integrations,
generator, get_template_source, read_config_file, CompileError, Config, Integrations, Syntax,
};
use proc_macro::TokenStream;
use proc_macro2::Span;

use std::collections::HashMap;
use std::path::PathBuf;

#[proc_macro_derive(Template, attributes(template))]
#[proc_macro_derive(Template, attributes(template, multi_template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
match build_template(&ast) {
Expand Down Expand Up @@ -42,7 +42,29 @@ fn build_template(ast: &syn::DeriveInput) -> Result<String, CompileError> {
};

let mut sources = HashMap::new();
find_used_templates(&input, &mut sources, source)?;
find_used_templates(
&config,
input.syntax,
&mut sources,
input.path.clone(),
source,
)?;

if let Some((_, multi)) = &input.multi {
for alternative in multi {
let source: String = match alternative.source {
Source::Source(ref s) => s.clone(),
Source::Path(_) => get_template_source(&alternative.path)?,
};
find_used_templates(
&config,
alternative.syntax,
&mut sources,
alternative.path.clone(),
source,
)?;
}
}

let mut parsed = HashMap::new();
for (path, src) in &sources {
Expand All @@ -54,36 +76,44 @@ fn build_template(ast: &syn::DeriveInput) -> Result<String, CompileError> {
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
};
let heritages = contexts
.iter()
.filter_map(
|(&path, ctx)| match !ctx.blocks.is_empty() || ctx.extends.is_some() {
true => Some((path, Heritage::new(ctx, &contexts))),
false => None,
},
)
.collect();

if input.print == Print::Ast || input.print == Print::All {
eprintln!("{:?}", parsed[input.path.as_path()]);
}

let code = generator::generate(&input, &contexts, heritage.as_ref(), INTEGRATIONS)?;
let code = generator::generate(&input, &contexts, &heritages, INTEGRATIONS)?;
if input.print == Print::Code || input.print == Print::All {
eprintln!("{}", code);
}
Ok(code)
}

fn find_used_templates(
input: &TemplateInput<'_>,
config: &Config<'_>,
syntax: &Syntax<'_>,
map: &mut HashMap<PathBuf, String>,
path: PathBuf,
source: String,
) -> Result<(), CompileError> {
let mut dependency_graph = Vec::new();
let mut check = vec![(input.path.clone(), source)];
let mut check = vec![(path, source)];
while let Some((path, source)) = check.pop() {
for n in parse(&source, input.syntax)? {
if map.contains_key(&path) {
continue;
}
for n in parse(&source, syntax)? {
match n {
Node::Extends(Expr::StrLit(extends)) => {
let extends = input.config.find_template(extends, Some(&path))?;
let extends = config.find_template(extends, Some(&path))?;
let dependency_path = (path.clone(), extends.clone());
if dependency_graph.contains(&dependency_path) {
return Err(CompileError::String(format!(
Expand All @@ -99,7 +129,7 @@ fn find_used_templates(
check.push((extends, source));
}
Node::Import(_, import, _) => {
let import = input.config.find_template(import, Some(&path))?;
let import = config.find_template(import, Some(&path))?;
let source = get_template_source(&import)?;
check.push((import, source));
}
Expand Down
Loading