1 use crate::errors
::{Error, Result}
;
2 use crate::parser
::ast
::MacroDefinition
;
3 use crate::template
::Template
;
5 use std
::collections
::HashMap
;
7 // Types around Macros get complicated, simplify it a bit by using aliases
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
>>;
16 /// Collection of all macro templates by file
17 #[derive(Clone, Debug, Default)]
18 pub struct MacroCollection
<'a
> {
19 macros
: MacroTemplateMap
<'a
>,
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() }
;
27 .add_macros_from_template(tera
, tpl
)
28 .expect("Couldn't load macros from base template");
33 /// Add macros from parsed template to `MacroCollection`
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(
42 template
: &'a Template
,
44 let template_name
= &template
.name
[..];
45 if self.macros
.contains_key(template_name
) {
49 let mut macro_namespace_map
= MacroNamespaceMap
::new();
51 if !template
.macros
.is_empty() {
52 macro_namespace_map
.insert("self", (template_name
, &template
.macros
));
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
)?
;
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
) {
65 // We inserted before so we're safe
66 macro_namespace_map
.insert(namespace
, *m
);
70 self.macros
.insert(template_name
, macro_namespace_map
);
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
)?
;
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
) {
82 // We inserted before so we're safe
83 self.macros
.get_mut(template_name
).unwrap().insert(namespace
, *m
);
92 template_name
: &'a
str,
93 macro_namespace
: &'a
str,
95 ) -> Result
<(&'a
str, &'a MacroDefinition
)> {
99 .and_then(|namespace_map
| namespace_map
.get(macro_namespace
));
101 if let Some(n
) = namespace
{
102 let &(macro_template
, macro_definition_map
) = n
;
104 if let Some(m
) = macro_definition_map
.get(macro_name
).map(|md
| (macro_template
, md
)) {
107 Err(Error
::msg(format
!(
108 "Macro `{}::{}` not found in template `{}`",
109 macro_namespace
, macro_name
, template_name
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