diff options
Diffstat (limited to 'askama_derive/src/lib.rs')
-rw-r--r-- | askama_derive/src/lib.rs | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs index 7c92fdc..df76507 100644 --- a/askama_derive/src/lib.rs +++ b/askama_derive/src/lib.rs @@ -1,8 +1,24 @@ extern crate askama_shared as shared; +#[macro_use] +extern crate nom; extern crate proc_macro; +#[macro_use] +extern crate quote; +#[macro_use(parse_quote)] extern crate syn; +mod input; +mod generator; +mod parser; + +use input::Print; +use parser::{Macro, Node}; use proc_macro::TokenStream; +use shared::path; + +use std::borrow::Cow; +use std::collections::HashMap; +use std::path::Path; #[proc_macro_derive(Template, attributes(template))] pub fn derive_template(input: TokenStream) -> TokenStream { @@ -11,5 +27,59 @@ pub fn derive_template(input: TokenStream) -> TokenStream { syn::Data::Struct(ref data) => data, _ => panic!("#[derive(Template)] can only be used with structs"), }; - shared::build_template(&ast).parse().unwrap() + build_template(&ast).parse().unwrap() +} + +/// 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) -> String { + let data = input::TemplateInput::new(ast); + let nodes = parser::parse(data.source.as_ref()); + let imports = Imports::new(&nodes, &data.path); + if data.meta.print == Print::Ast || data.meta.print == Print::All { + println!("{:?}", nodes); + } + let code = generator::generate(&data, &nodes, &imports.macro_map()); + if data.meta.print == Print::Code || data.meta.print == Print::All { + println!("{}", code); + } + code +} + +struct Imports<'a> { + sources: HashMap<&'a str, Cow<'a, str>>, +} + +impl<'a> Imports<'a> { + fn new(parent_nodes: &'a [Node], parent_path: &'a Path) -> Imports<'a> { + let sources = parent_nodes.iter().filter_map(|n| { + match *n { + Node::Import(_, import_path, scope) => { + let path = path::find_template_from_path(import_path, Some(parent_path)); + let src = path::get_template_source(&path); + Some((scope, Cow::Owned(src))) + }, + _ => None, + } + }).collect(); + Imports { sources } + } + + fn macro_map(&'a self) -> HashMap<(&'a str, &'a str), Macro<'a>> { + let mut macro_map = HashMap::new(); + for (scope, s) in &self.sources { + for n in parser::parse(s.as_ref()) { + match n { + Node::Macro(name, m) => macro_map.insert((*scope, name), m), + _ => None, + }; + } + } + macro_map + } } |