Skip to content

Commit

Permalink
Merge derive.rs into generator.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
Kijewski authored and djc committed Mar 23, 2022
1 parent 62019c2 commit b14982f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 119 deletions.
106 changes: 0 additions & 106 deletions askama_shared/src/derive.rs

This file was deleted.

110 changes: 99 additions & 11 deletions askama_shared/src/generator.rs
Original file line number Diff line number Diff line change
@@ -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<S: std::hash::BuildHasher>(
input: &TemplateInput<'_>,
contexts: &HashMap<&Path, Context<'_>, S>,
heritage: Option<&Heritage<'_>>,
) -> Result<String, CompileError> {
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<String, CompileError> {
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<PathBuf, String>,
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::<Vec<String>>()
)
.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>,
Expand Down
3 changes: 1 addition & 2 deletions askama_shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit b14982f

Please sign in to comment.