From df58dcb49db77b2c283db3c9a8a738bdff3a8e37 Mon Sep 17 00:00:00 2001 From: larros Date: Tue, 26 Sep 2017 10:02:44 +0200 Subject: Add support for importing template files with macros (#51) * Fix review comments for macro imports --- askama_shared/src/generator.rs | 10 +++++++--- askama_shared/src/lib.rs | 43 +++++++++++++++++++++++++++++++++++++++++- askama_shared/src/parser.rs | 13 +++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) (limited to 'askama_shared') diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index e92a2c6..e135c13 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -12,8 +12,8 @@ use std::collections::{HashMap, HashSet}; use syn; -pub fn generate(input: &TemplateInput, nodes: &[Node]) -> String { - Generator::default().build(&State::new(input, nodes)) +pub fn generate(input: &TemplateInput, nodes: &[Node], imported: &HashMap<&str, Macro>) -> String { + Generator::default().build(&State::new(input, nodes, imported)) } struct State<'a> { @@ -26,7 +26,7 @@ struct State<'a> { } impl<'a> State<'a> { - fn new<'n>(input: &'n TemplateInput, nodes: &'n [Node]) -> State<'n> { + fn new<'n>(input: &'n TemplateInput, nodes: &'n [Node], imported: &'n HashMap<&'n str, Macro<'n>>) -> State<'n> { let mut base: Option<&Expr> = None; let mut blocks = Vec::new(); let mut macros = HashMap::new(); @@ -47,6 +47,9 @@ impl<'a> State<'a> { _ => {}, } } + for (name, ref m) in imported { + macros.insert(name, m); + } State { input, nodes, @@ -351,6 +354,7 @@ impl<'a> Generator<'a> { }, Node::Call(ref ws, name, ref args) => self.write_call(state, ws, name, args), Node::Macro(_, _) | + Node::Import(_, _) | Node::Extends(_) => { if let AstLevel::Nested = level { panic!("macro or extend blocks only allowed at the top level"); diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index 066faca..a7deaff 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -21,7 +21,11 @@ mod input; mod parser; use input::Print; +use parser::{Node, Macro}; +use std::borrow::Cow; +use std::collections::HashMap; +use std::path::Path; /// Takes a `syn::DeriveInput` and generates source code for it /// @@ -33,16 +37,53 @@ use input::Print; pub 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); + let imported = imports.parse(); if data.meta.print == Print::Ast || data.meta.print == Print::All { println!("{:?}", nodes); } - let code = generator::generate(&data, &nodes); + let code = generator::generate(&data, &nodes, &imported); if data.meta.print == Print::Code || data.meta.print == Print::All { println!("{}", code); } code } + +pub struct Imports<'a> { + pub sources: Vec> +} + +impl <'a> Imports<'a> { + pub fn new(parent_nodes: &'a [Node], parent_path: &'a Path) -> Imports<'a> { + let sources = parent_nodes.iter().filter_map(|n| { + match *n { + Node::Import(_, ref import_path) => { + let path = path::find_template_from_path(import_path, Some(parent_path)); + let src = path::get_template_source(&path); + Some(Cow::Owned(src)) + }, + _ => None, + } + }).collect(); + Imports { + sources, + } + } + + pub fn parse(&'a self) -> HashMap<&'a str, Macro<'a>> { + self.sources.iter() + .flat_map(|s| parser::parse(s.as_ref())) + .filter_map(|n| { + match n { + Node::Macro(name, m) => Some((name, m)), + _ => None, + }}) + .collect() + } +} + + mod errors { error_chain! { foreign_links { diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index ab79a31..083e96b 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -42,6 +42,7 @@ pub enum Node<'a> { Extends(Expr<'a>), BlockDef(WS, &'a str, Vec>, WS), Include(WS, &'a str), + Import(WS, &'a str), Macro(&'a str, Macro<'a>), } @@ -388,6 +389,17 @@ named!(block_include, do_parse!( })) )); +named!(block_import, do_parse!( + pws: opt!(tag_s!("-")) >> + ws!(tag_s!("import")) >> + name: ws!(expr_str_lit) >> + nws: opt!(tag_s!("-")) >> + (Node::Import(WS(pws.is_some(), nws.is_some()), match name { + Expr::StrLit(s) => s, + _ => panic!("import path must be a string literal"), + })) +)); + named!(block_macro, do_parse!( pws1: opt!(tag_s!("-")) >> ws!(tag_s!("macro")) >> @@ -420,6 +432,7 @@ named!(block_node, do_parse!( block_for | block_extends | block_include | + block_import | block_block | block_macro ) >> -- cgit