use std::collections::{HashMap, VecDeque};
use parser::{parse, Node};
use errors::{Result};
#[derive(Debug, Clone)]
pub struct Template {
pub name: String,
pub path: Option<String>,
pub ast: Node,
pub macros: HashMap<String, Node>,
pub imported_macro_files: Vec<(String, String)>,
pub parent: Option<String>,
pub blocks: HashMap<String, Node>,
pub parents: Vec<String>,
pub blocks_definitions: HashMap<String, Vec<(String, Node)>>,
pub from_extend: bool,
}
impl Template {
pub fn new(tpl_name: &str, tpl_path: Option<String>, input: &str) -> Result<Template> {
let ast = parse(input)?;
let mut blocks = HashMap::new();
fn find_blocks(ast: &VecDeque<Node>, blocks: &mut HashMap<String, Node>) -> Result<()> {
for node in ast {
match *node {
Node::Block { ref name, ref body } => {
if blocks.contains_key(name) {
bail!("Block `{}` is duplicated", name);
}
blocks.insert(name.to_string(), node.clone());
find_blocks(body.get_children(), blocks)?;
},
_ => continue,
};
}
Ok(())
}
find_blocks(ast.get_children(), &mut blocks)?;
let mut macros = HashMap::new();
let mut imported_macro_files = vec![];
let mut parent = None;
for node in ast.get_children() {
match *node {
Node::Extends(ref name) => {
parent = Some(name.to_string());
},
Node::Macro { ref name, .. } => {
if macros.contains_key(name) {
bail!("Macro `{}` is duplicated", name);
}
macros.insert(name.to_string(), node.clone());
},
Node::ImportMacro { ref tpl_name, ref name } => {
imported_macro_files.push((tpl_name.to_string(), name.to_string()));
}
_ => continue,
};
}
Ok(Template {
name: tpl_name.to_string(),
path: tpl_path,
ast: ast,
parent: parent,
blocks: blocks,
macros: macros,
imported_macro_files: imported_macro_files,
parents: vec![],
blocks_definitions: HashMap::new(),
from_extend: false,
})
}
}
#[cfg(test)]
mod tests {
use super::Template;
#[test]
fn test_can_parse_ok_template() {
Template::new("hello", None, "Hello {{ world }}.").unwrap();
}
#[test]
fn test_can_find_parent_template() {
let tpl = Template::new("hello", None,"{% extends \"base.html\" %}").unwrap();
assert_eq!(tpl.parent.unwrap(), "base.html".to_string());
}
#[test]
fn test_can_find_blocks() {
let tpl = Template::new(
"hello",
None,
"{% extends \"base.html\" %}{% block hey %}{% endblock hey %}"
).unwrap();
assert_eq!(tpl.parent.unwrap(), "base.html".to_string());
assert_eq!(tpl.blocks.contains_key("hey"), true);
}
#[test]
fn test_can_find_nested_blocks() {
let tpl = Template::new(
"hello",
None,
"{% extends \"base.html\" %}{% block hey %}{% block extrahey %}{% endblock extrahey %}{% endblock hey %}"
).unwrap();
assert_eq!(tpl.parent.unwrap(), "base.html".to_string());
assert_eq!(tpl.blocks.contains_key("hey"), true);
assert_eq!(tpl.blocks.contains_key("extrahey"), true);
}
#[test]
fn test_can_find_macros() {
let tpl = Template::new("hello", None, "{% macro hey() %}{% endmacro hey %}").unwrap();
assert_eq!(tpl.macros.contains_key("hey"), true);
}
#[test]
fn test_can_find_imported_macros() {
let tpl = Template::new("hello", None, "{% import \"macros.html\" as macros %}").unwrap();
assert_eq!(tpl.imported_macro_files, vec![("macros.html".to_string(), "macros".to_string())]);
}
}