]>
Commit | Line | Data |
---|---|---|
136023e0 XL |
1 | use crate::errors::{Error, Result}; |
2 | use crate::parser::ast::MacroDefinition; | |
3 | use crate::template::Template; | |
4 | use crate::tera::Tera; | |
5 | use std::collections::HashMap; | |
6 | ||
7 | // Types around Macros get complicated, simplify it a bit by using aliases | |
8 | ||
9 | /// Maps { macro => macro_definition } | |
10 | pub type MacroDefinitionMap = HashMap<String, MacroDefinition>; | |
11 | /// Maps { namespace => ( macro_template, { macro => macro_definition }) } | |
12 | pub type MacroNamespaceMap<'a> = HashMap<&'a str, (&'a str, &'a MacroDefinitionMap)>; | |
13 | /// Maps { template => { namespace => ( macro_template, { macro => macro_definition }) } | |
14 | pub type MacroTemplateMap<'a> = HashMap<&'a str, MacroNamespaceMap<'a>>; | |
15 | ||
16 | /// Collection of all macro templates by file | |
17 | #[derive(Clone, Debug, Default)] | |
18 | pub struct MacroCollection<'a> { | |
19 | macros: MacroTemplateMap<'a>, | |
20 | } | |
21 | ||
22 | impl<'a> MacroCollection<'a> { | |
23 | pub fn from_original_template(tpl: &'a Template, tera: &'a Tera) -> MacroCollection<'a> { | |
24 | let mut macro_collection = MacroCollection { macros: MacroTemplateMap::new() }; | |
25 | ||
26 | macro_collection | |
27 | .add_macros_from_template(tera, tpl) | |
28 | .expect("Couldn't load macros from base template"); | |
29 | ||
30 | macro_collection | |
31 | } | |
32 | ||
33 | /// Add macros from parsed template to `MacroCollection` | |
34 | /// | |
35 | /// Macro templates can import other macro templates so the macro loading needs to | |
36 | /// happen recursively. We need all of the macros loaded in one go to be in the same | |
37 | /// HashMap for easy popping as well, otherwise there could be stray macro | |
38 | /// definitions remaining | |
39 | pub fn add_macros_from_template( | |
40 | &mut self, | |
41 | tera: &'a Tera, | |
42 | template: &'a Template, | |
43 | ) -> Result<()> { | |
44 | let template_name = &template.name[..]; | |
45 | if self.macros.contains_key(template_name) { | |
46 | return Ok(()); | |
47 | } | |
48 | ||
49 | let mut macro_namespace_map = MacroNamespaceMap::new(); | |
50 | ||
51 | if !template.macros.is_empty() { | |
52 | macro_namespace_map.insert("self", (template_name, &template.macros)); | |
53 | } | |
54 | ||
55 | for &(ref filename, ref namespace) in &template.imported_macro_files { | |
56 | let macro_tpl = tera.get_template(filename)?; | |
57 | macro_namespace_map.insert(namespace, (filename, ¯o_tpl.macros)); | |
58 | self.add_macros_from_template(tera, macro_tpl)?; | |
59 | ||
60 | // We need to load the macros loaded in our macros in our namespace as well, unless we override it | |
61 | for (namespace, m) in &self.macros[¯o_tpl.name.as_ref()].clone() { | |
62 | if macro_namespace_map.contains_key(namespace) { | |
63 | continue; | |
64 | } | |
65 | // We inserted before so we're safe | |
66 | macro_namespace_map.insert(namespace, *m); | |
67 | } | |
68 | } | |
69 | ||
70 | self.macros.insert(template_name, macro_namespace_map); | |
71 | ||
72 | for parent in &template.parents { | |
73 | let parent = &parent[..]; | |
74 | let parent_template = tera.get_template(parent)?; | |
75 | self.add_macros_from_template(tera, parent_template)?; | |
76 | ||
77 | // We need to load the parent macros in our namespace as well, unless we override it | |
78 | for (namespace, m) in &self.macros[parent].clone() { | |
79 | if self.macros[template_name].contains_key(namespace) { | |
80 | continue; | |
81 | } | |
82 | // We inserted before so we're safe | |
83 | self.macros.get_mut(template_name).unwrap().insert(namespace, *m); | |
84 | } | |
85 | } | |
86 | ||
87 | Ok(()) | |
88 | } | |
89 | ||
90 | pub fn lookup_macro( | |
91 | &self, | |
92 | template_name: &'a str, | |
93 | macro_namespace: &'a str, | |
94 | macro_name: &'a str, | |
95 | ) -> Result<(&'a str, &'a MacroDefinition)> { | |
96 | let namespace = self | |
97 | .macros | |
98 | .get(template_name) | |
99 | .and_then(|namespace_map| namespace_map.get(macro_namespace)); | |
100 | ||
101 | if let Some(n) = namespace { | |
102 | let &(macro_template, macro_definition_map) = n; | |
103 | ||
104 | if let Some(m) = macro_definition_map.get(macro_name).map(|md| (macro_template, md)) { | |
105 | Ok(m) | |
106 | } else { | |
107 | Err(Error::msg(format!( | |
108 | "Macro `{}::{}` not found in template `{}`", | |
109 | macro_namespace, macro_name, template_name | |
110 | ))) | |
111 | } | |
112 | } else { | |
113 | Err(Error::msg(format!( | |
114 | "Macro namespace `{}` was not found in template `{}`. Have you maybe forgotten to import it, or misspelled it?", | |
115 | macro_namespace, template_name | |
116 | ))) | |
117 | } | |
118 | } | |
119 | } |