]> git.proxmox.com Git - rustc.git/blob - vendor/tera/src/renderer/macros.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / vendor / tera / src / renderer / macros.rs
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, &macro_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[&macro_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 }