From a32532d5ae339cf3dcde1bd0b684e7bd7f8c51fc Mon Sep 17 00:00:00 2001 From: Lars Erik Rosengren Date: Sun, 15 Oct 2017 13:09:07 +0200 Subject: Add scope when importing macros --- askama_shared/src/generator.rs | 26 ++++++++++++++++---------- askama_shared/src/lib.rs | 22 ++++++++++++---------- askama_shared/src/parser.rs | 15 +++++++++++---- testing/templates/import.html | 4 ++-- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index df01dfe..544d336 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -12,7 +12,7 @@ use std::collections::{HashMap, HashSet}; use syn; -pub fn generate(input: &TemplateInput, nodes: &[Node], imported: &HashMap<&str, Macro>) -> String { +pub fn generate(input: &TemplateInput, nodes: &[Node], imported: &HashMap<(&str, &str), Macro>) -> String { Generator::default().build(&State::new(input, nodes, imported)) } @@ -26,7 +26,7 @@ struct State<'a> { } impl<'a> State<'a> { - fn new<'n>(input: &'n TemplateInput, nodes: &'n [Node], imported: &'n HashMap<&'n str, Macro<'n>>) -> State<'n> { + fn new<'n>(input: &'n TemplateInput, nodes: &'n [Node], imported: &'n HashMap<(&'n str, &'n str), Macro<'n>>) -> State<'n> { let mut base: Option<&Expr> = None; let mut blocks = Vec::new(); let mut macros = HashMap::new(); @@ -42,13 +42,13 @@ impl<'a> State<'a> { blocks.push(def); }, Node::Macro(name, ref m) => { - macros.insert(name, m); + macros.insert((None, name), m); }, _ => {}, } } - for (name, ref m) in imported { - macros.insert(name, m); + for (&(scope, name), ref m) in imported { + macros.insert((Some(scope), name), m); } State { input, @@ -351,7 +351,7 @@ impl<'a> Generator<'a> { Node::Include(ref ws, path) => { self.handle_include(state, ws, path); }, - Node::Call(ref ws, name, ref args) => self.write_call(state, ws, name, args), + Node::Call(ref ws, scope, name, ref args) => self.write_call(state, ws, scope, name, args), Node::Macro(_, ref m) => { if let AstLevel::Nested = level { panic!("macro blocks only allowed at the top level"); @@ -359,7 +359,7 @@ impl<'a> Generator<'a> { self.flush_ws(&m.ws1); self.prepare_ws(&m.ws2); }, - Node::Import(ref ws, _) => { + Node::Import(ref ws, _, _) => { if let AstLevel::Nested = level { panic!("import blocks only allowed at the top level"); } @@ -446,8 +446,14 @@ impl<'a> Generator<'a> { self.locals.pop(); } - fn write_call(&mut self, state: &'a State, ws: &WS, name: &str, args: &[Expr]) { - let def = state.macros.get(name).expect(&format!("macro '{}' not found", name)); + fn write_call(&mut self, state: &'a State, ws: &WS, scope: Option<&str>, name: &str, args: &[Expr]) { + let def = state.macros.get(&(scope, name)).unwrap_or_else(|| { + if let Some(ref s) = scope { + panic!(format!("macro '{}::{}' not found", s, name)); + } else { + panic!(format!("macro '{}' not found", name)); + } + }); self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first self.locals.push(); self.writeln("{"); @@ -812,4 +818,4 @@ enum DisplayWrap { Unwrapped, } -type MacroMap<'a> = HashMap<&'a str, &'a Macro<'a>>; +type MacroMap<'a> = HashMap<(Option<&'a str>, &'a str), &'a Macro<'a>>; diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index 4e57a82..88439dc 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -50,17 +50,17 @@ pub fn build_template(ast: &syn::DeriveInput) -> String { pub struct Imports<'a> { - pub sources: Vec> + pub sources: HashMap<&'a str, Cow<'a, str>> } 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) => { + Node::Import(_, ref import_path, scope) => { let path = path::find_template_from_path(import_path, Some(parent_path)); let src = path::get_template_source(&path); - Some(Cow::Owned(src)) + Some((scope, Cow::Owned(src))) }, _ => None, } @@ -68,15 +68,17 @@ impl <'a> Imports<'a> { Imports { sources } } - pub fn macro_map(&'a self) -> HashMap<&'a str, Macro<'a>> { - self.sources.iter() - .flat_map(|s| parser::parse(s.as_ref())) - .filter_map(|n| { + pub fn macro_map(&'a self) -> HashMap<(&'a str, &'a str), Macro<'a>> { + let mut macro_map = HashMap::new(); + for (scope, s) in self.sources.iter() { + for n in parser::parse(&s.as_ref()) { match n { - Node::Macro(name, m) => Some((name, m)), + Node::Macro(name, m) => macro_map.insert((*scope, name), m), _ => None, - }}) - .collect() + }; + } + } + macro_map } } diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index 495b68f..7cd8936 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -35,7 +35,7 @@ pub enum Node<'a> { Lit(&'a str, &'a str, &'a str), Comment(), Expr(WS, Expr<'a>), - Call(WS, &'a str, Vec>), + Call(WS, Option<& 'a str>, &'a str, Vec>), LetDecl(WS, Target<'a>), Let(WS, Target<'a>, Expr<'a>), Cond(Vec<(WS, Option>, Vec>)>, WS), @@ -43,7 +43,7 @@ pub enum Node<'a> { Extends(Expr<'a>), BlockDef(WS, &'a str, Vec>, WS), Include(WS, &'a str), - Import(WS, &'a str), + Import(WS, &'a str, &'a str), Macro(&'a str, Macro<'a>), } @@ -296,10 +296,15 @@ named!(expr_node, do_parse!( named!(block_call, do_parse!( pws: opt!(tag_s!("-")) >> ws!(tag_s!("call")) >> + scope: opt!(do_parse!( + scope: ws!(identifier) >> + ws!(tag_s!("::")) >> + (scope) + )) >> name: ws!(identifier) >> args: ws!(arguments) >> nws: opt!(tag_s!("-")) >> - (Node::Call(WS(pws.is_some(), nws.is_some()), name, args)) + (Node::Call(WS(pws.is_some(), nws.is_some()), scope, name, args)) )); named!(cond_if, do_parse!( @@ -411,11 +416,13 @@ named!(block_import, do_parse!( pws: opt!(tag_s!("-")) >> ws!(tag_s!("import")) >> name: ws!(expr_str_lit) >> + ws!(tag_s!("as")) >> + scope: ws!(identifier) >> 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"), - })) + }, scope)) )); named!(block_macro, do_parse!( diff --git a/testing/templates/import.html b/testing/templates/import.html index 51ddd2f..85538ad 100644 --- a/testing/templates/import.html +++ b/testing/templates/import.html @@ -1,4 +1,4 @@ -{%- import "macro.html" -%} +{%- import "macro.html" as scope -%} -{% call thrice(s) %} +{% call scope::thrice(s) %} -- cgit