use std::slice::Iter;
use std::iter::Peekable;
use std::convert::From;
use std::collections::{BTreeMap, VecDeque};
use pest::prelude::*;
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::Json;
#[cfg(feature = "serde_type")]
use serde_json::value::Value as Json;
#[cfg(feature = "serde_type")]
use std::str::FromStr;
use grammar::{Rdp, Rule};
use error::{TemplateError, TemplateErrorReason};
use self::TemplateElement::*;
#[derive(PartialEq, Clone, Debug)]
pub struct TemplateMapping(pub usize, pub usize);
#[derive(PartialEq, Clone, Debug)]
pub struct Template {
pub name: Option<String>,
pub elements: Vec<TemplateElement>,
pub mapping: Option<Vec<TemplateMapping>>,
}
#[derive(PartialEq, Clone, Debug)]
pub struct Subexpression {
pub name: String,
pub params: Vec<Parameter>,
pub hash: BTreeMap<String, Parameter>,
}
impl Subexpression {
pub fn is_helper(&self) -> bool {
!(self.params.is_empty() && self.hash.is_empty())
}
pub fn as_template(&self) -> Template {
let mut t = Template::new(false);
let el = if self.is_helper() {
HelperExpression(HelperTemplate::from(self))
} else {
Expression(Parameter::Name(self.name.clone()))
};
t.elements.push(el);
t
}
}
#[derive(PartialEq, Clone, Debug)]
pub enum BlockParam {
Single(Parameter),
Pair((Parameter, Parameter)),
}
#[derive(PartialEq, Clone, Debug)]
pub struct ExpressionSpec {
pub name: Parameter,
pub params: Vec<Parameter>,
pub hash: BTreeMap<String, Parameter>,
pub block_param: Option<BlockParam>,
pub omit_pre_ws: bool,
pub omit_pro_ws: bool,
}
#[derive(PartialEq, Clone, Debug)]
pub enum Parameter {
Name(String),
Literal(Json),
Subexpression(Subexpression),
}
#[derive(PartialEq, Clone, Debug)]
pub struct HelperTemplate {
pub name: String,
pub params: Vec<Parameter>,
pub hash: BTreeMap<String, Parameter>,
pub block_param: Option<BlockParam>,
pub template: Option<Template>,
pub inverse: Option<Template>,
pub block: bool,
}
impl<'a> From<&'a Subexpression> for HelperTemplate {
fn from(s: &Subexpression) -> HelperTemplate {
HelperTemplate {
name: s.name.clone(),
params: s.params.clone(),
hash: s.hash.clone(),
block_param: None,
template: None,
inverse: None,
block: false,
}
}
}
#[derive(PartialEq, Clone, Debug)]
pub struct Directive {
pub name: Parameter,
pub params: Vec<Parameter>,
pub hash: BTreeMap<String, Parameter>,
pub template: Option<Template>,
}
impl Parameter {
pub fn as_name(self) -> Option<String> {
if let Parameter::Name(n) = self {
Some(n)
} else {
None
}
}
pub fn parse(s: &str) -> Result<Parameter, TemplateError> {
let mut parser = Rdp::new(StringInput::new(s));
if !parser.parameter() {
return Err(TemplateError::of(TemplateErrorReason::InvalidParam(s.to_owned())));
}
let mut it = parser.queue().iter().peekable();
Template::parse_param(s, &mut it, s.len() - 1)
}
}
impl Template {
pub fn new(mapping: bool) -> Template {
Template {
elements: Vec::new(),
name: None,
mapping: if mapping {
Some(Vec::new())
} else {
None
},
}
}
fn push_element(&mut self, e: TemplateElement, line: usize, col: usize) {
self.elements.push(e);
if let Some(ref mut maps) = self.mapping {
maps.push(TemplateMapping(line, col));
}
}
pub fn compile<S: AsRef<str>>(source: S) -> Result<Template, TemplateError> {
Template::compile2(source, false)
}
#[inline]
fn parse_subexpression<'a>(source: &'a str,
it: &mut Peekable<Iter<Token<Rule>>>,
limit: usize)
-> Result<Parameter, TemplateError> {
let espec = try!(Template::parse_expression(source, it.by_ref(), limit));
if let Parameter::Name(name) = espec.name {
Ok(Parameter::Subexpression(Subexpression {
name: name,
params: espec.params,
hash: espec.hash,
}))
} else {
Err(TemplateError::of(TemplateErrorReason::NestedSubexpression))
}
}
#[inline]
fn parse_name<'a>(source: &'a str,
it: &mut Peekable<Iter<Token<Rule>>>,
_: usize)
-> Result<Parameter, TemplateError> {
let name_node = it.next().unwrap();
match name_node.rule {
Rule::identifier | Rule::reference | Rule::invert_tag_item => {
Ok(Parameter::Name(source[name_node.start..name_node.end].to_owned()))
}
Rule::subexpression => {
Template::parse_subexpression(source, it.by_ref(), name_node.end)
}
_ => unreachable!(),
}
}
#[inline]
fn parse_param<'a>(source: &'a str,
it: &mut Peekable<Iter<Token<Rule>>>,
_: usize)
-> Result<Parameter, TemplateError> {
let mut param = it.next().unwrap();
if param.rule == Rule::param {
param = it.next().unwrap();
}
let result = match param.rule {
Rule::reference => Parameter::Name(source[param.start..param.end].to_owned()),
Rule::literal => {
let s = &source[param.start..param.end];
if let Ok(json) = Json::from_str(s) {
Parameter::Literal(json)
} else {
Parameter::Name(s.to_owned())
}
}
Rule::subexpression => {
try!(Template::parse_subexpression(source, it.by_ref(), param.end))
}
_ => unreachable!(),
};
loop {
if let Some(ref n) = it.peek() {
if n.end > param.end {
break;
}
} else {
break;
}
it.next();
}
Ok(result)
}
#[inline]
fn parse_hash<'a>(source: &'a str,
it: &mut Peekable<Iter<Token<Rule>>>,
limit: usize)
-> Result<(String, Parameter), TemplateError> {
let name = it.next().unwrap();
let key = source[name.start..name.end].to_owned();
let value = try!(Template::parse_param(source, it.by_ref(), limit));
Ok((key, value))
}
#[inline]
fn parse_block_param<'a>(source: &'a str,
it: &mut Peekable<Iter<Token<Rule>>>,
limit: usize)
-> Result<BlockParam, TemplateError> {
let p1_name = it.next().unwrap();
let p1 = source[p1_name.start..p1_name.end].to_owned();
let p2 = it.peek().and_then(|p2_name| {
if p2_name.end <= limit {
Some(source[p2_name.start..p2_name.end].to_owned())
} else {
None
}
});
if p2.is_some() {
it.next();
Ok(BlockParam::Pair((Parameter::Name(p1), Parameter::Name(p2.unwrap()))))
} else {
Ok(BlockParam::Single(Parameter::Name(p1)))
}
}
#[inline]
fn parse_expression<'a>(source: &'a str,
it: &mut Peekable<Iter<Token<Rule>>>,
limit: usize)
-> Result<ExpressionSpec, TemplateError> {
let mut params: Vec<Parameter> = Vec::new();
let mut hashes: BTreeMap<String, Parameter> = BTreeMap::new();
let mut omit_pre_ws = false;
let mut omit_pro_ws = false;
let mut block_param = None;
if it.peek().unwrap().rule == Rule::pre_whitespace_omitter {
omit_pre_ws = true;
it.next();
}
let name = try!(Template::parse_name(source, it.by_ref(), limit));
loop {
let rule;
let end;
if let Some(ref token) = it.peek() {
if token.end < limit {
rule = token.rule;
end = token.end;
} else {
break;
}
} else {
break;
}
it.next();
match rule {
Rule::param => {
params.push(try!(Template::parse_param(source, it.by_ref(), end)));
}
Rule::hash => {
let (key, value) = try!(Template::parse_hash(source, it.by_ref(), end));
hashes.insert(key, value);
}
Rule::block_param => {
block_param = Some(try!(Template::parse_block_param(source, it.by_ref(), end)));
}
Rule::pro_whitespace_omitter => {
omit_pro_ws = true;
}
_ => {}
}
}
Ok(ExpressionSpec {
name: name,
params: params,
hash: hashes,
block_param: block_param,
omit_pre_ws: omit_pre_ws,
omit_pro_ws: omit_pro_ws,
})
}
#[inline]
fn remove_previous_whitespace(template_stack: &mut VecDeque<Template>) {
let mut t = template_stack.front_mut().unwrap();
if let Some(el) = t.elements.pop() {
if let RawString(ref text) = el {
t.elements.push(RawString(text.trim_right().to_owned()));
} else {
t.elements.push(el);
}
}
}
pub fn compile2<S: AsRef<str>>(source: S, mapping: bool) -> Result<Template, TemplateError> {
let source = source.as_ref();
let mut helper_stack: VecDeque<HelperTemplate> = VecDeque::new();
let mut directive_stack: VecDeque<Directive> = VecDeque::new();
let mut template_stack: VecDeque<Template> = VecDeque::new();
let mut omit_pro_ws = false;
let input = StringInput::new(source);
let mut parser = Rdp::new(input);
if !parser.handlebars() {
let (_, pos) = parser.expected();
let (line_no, col_no) = parser.input().line_col(pos);
return Err(TemplateError::of(TemplateErrorReason::InvalidSyntax).at(line_no, col_no));
}
let mut it = parser.queue().iter().peekable();
let mut prev_end = 0;
loop {
if let Some(ref token) = it.next() {
if token.rule != Rule::template {
if token.start != prev_end && !omit_pro_ws && token.rule != Rule::raw_text &&
token.rule != Rule::raw_block_text {
let (line_no, col_no) = parser.input().line_col(prev_end);
if token.rule == Rule::raw_block_end {
let text = &source[prev_end..token.start];
let mut t = Template::new(mapping);
t.push_element(RawString(text.to_owned()), line_no, col_no);
template_stack.push_front(t);
} else {
let text = &source[prev_end..token.start];
let mut t = template_stack.front_mut().unwrap();
t.push_element(RawString(text.to_owned()), line_no, col_no);
}
}
}
let (line_no, col_no) = parser.input().line_col(token.start);
match token.rule {
Rule::template => {
template_stack.push_front(Template::new(mapping));
}
Rule::raw_text => {
let mut text = &source[prev_end..token.end];
if omit_pro_ws {
text = text.trim_left();
}
let mut t = template_stack.front_mut().unwrap();
t.push_element(RawString(text.to_owned()), line_no, col_no);
}
Rule::helper_block_start |
Rule::raw_block_start |
Rule::directive_block_start |
Rule::partial_block_start => {
let exp = try!(Template::parse_expression(source, it.by_ref(), token.end));
match token.rule {
Rule::helper_block_start |
Rule::raw_block_start => {
let helper_template = HelperTemplate {
name: exp.name.as_name().unwrap(),
params: exp.params,
hash: exp.hash,
block_param: exp.block_param,
block: true,
template: None,
inverse: None,
};
helper_stack.push_front(helper_template);
}
Rule::directive_block_start |
Rule::partial_block_start => {
let directive = Directive {
name: exp.name,
params: exp.params,
hash: exp.hash,
template: None,
};
directive_stack.push_front(directive);
}
_ => unreachable!(),
}
if exp.omit_pre_ws {
Template::remove_previous_whitespace(&mut template_stack);
}
omit_pro_ws = exp.omit_pro_ws;
let mut t = template_stack.front_mut().unwrap();
if let Some(ref mut maps) = t.mapping {
maps.push(TemplateMapping(line_no, col_no));
}
}
Rule::invert_tag => {
let exp = try!(Template::parse_expression(source, it.by_ref(), token.end));
if exp.omit_pre_ws {
Template::remove_previous_whitespace(&mut template_stack);
}
omit_pro_ws = exp.omit_pro_ws;
let t = template_stack.pop_front().unwrap();
let mut h = helper_stack.front_mut().unwrap();
h.template = Some(t);
}
Rule::raw_block_text => {
let mut text = &source[prev_end..token.end];
if omit_pro_ws {
text = text.trim_left();
}
let mut t = Template::new(mapping);
t.push_element(RawString(text.to_owned()), line_no, col_no);
template_stack.push_front(t);
}
Rule::expression |
Rule::html_expression |
Rule::helper_expression |
Rule::directive_expression |
Rule::partial_expression |
Rule::helper_block_end |
Rule::raw_block_end |
Rule::directive_block_end |
Rule::partial_block_end => {
let exp = try!(Template::parse_expression(source, it.by_ref(), token.end));
if exp.omit_pre_ws {
Template::remove_previous_whitespace(&mut template_stack);
}
omit_pro_ws = exp.omit_pro_ws;
match token.rule {
Rule::expression => {
let el = Expression(exp.name);
let mut t = template_stack.front_mut().unwrap();
t.push_element(el, line_no, col_no);
}
Rule::html_expression => {
let el = HTMLExpression(exp.name);
let mut t = template_stack.front_mut().unwrap();
t.push_element(el, line_no, col_no);
}
Rule::helper_expression => {
let helper_template = HelperTemplate {
name: exp.name.as_name().unwrap(),
params: exp.params,
hash: exp.hash,
block_param: exp.block_param,
block: false,
template: None,
inverse: None,
};
let el = HelperExpression(helper_template);
let mut t = template_stack.front_mut().unwrap();
t.push_element(el, line_no, col_no);
}
Rule::directive_expression |
Rule::partial_expression => {
let directive = Directive {
name: exp.name,
params: exp.params,
hash: exp.hash,
template: None,
};
let el = if token.rule == Rule::directive_expression {
DirectiveExpression(directive)
} else {
PartialExpression(directive)
};
let mut t = template_stack.front_mut().unwrap();
t.push_element(el, line_no, col_no);
}
Rule::helper_block_end |
Rule::raw_block_end => {
let mut h = helper_stack.pop_front().unwrap();
let close_tag_name = exp.name.as_name().unwrap();
if h.name == close_tag_name {
let prev_t = template_stack.pop_front().unwrap();
if h.template.is_some() {
h.inverse = Some(prev_t);
} else {
h.template = Some(prev_t);
}
let t = template_stack.front_mut().unwrap();
t.elements.push(HelperBlock(h));
} else {
return Err(TemplateError::of(
TemplateErrorReason::MismatchingClosedHelper(
h.name, close_tag_name)).at(line_no, col_no));
}
}
Rule::directive_block_end |
Rule::partial_block_end => {
let mut d = directive_stack.pop_front().unwrap();
let close_tag_name = exp.name;
if d.name == close_tag_name {
let prev_t = template_stack.pop_front().unwrap();
d.template = Some(prev_t);
let t = template_stack.front_mut().unwrap();
if token.rule == Rule::directive_block_end {
t.elements.push(DirectiveBlock(d));
} else {
t.elements.push(PartialBlock(d));
}
} else {
return Err(TemplateError::of(
TemplateErrorReason::MismatchingClosedDirective(
d.name, close_tag_name)).at(line_no, col_no));
}
}
_ => unreachable!(),
}
}
Rule::hbs_comment => {
let text = parser.input().slice(token.start + 3, token.end - 2);
let mut t = template_stack.front_mut().unwrap();
t.push_element(Comment(text.to_owned()), line_no, col_no);
}
_ => {}
}
if token.rule != Rule::template {
prev_end = token.end;
}
} else {
if prev_end < source.len() {
let text = &source[prev_end..source.len()];
let (line_no, col_no) = parser.input().line_col(prev_end);
let mut t = template_stack.front_mut().unwrap();
t.push_element(RawString(text.to_owned()), line_no, col_no);
}
return Ok(template_stack.pop_front().unwrap());
}
}
}
pub fn compile_with_name<S: AsRef<str>>(source: S,
name: String,
mapping: bool)
-> Result<Template, TemplateError> {
match Template::compile2(source, mapping) {
Ok(mut t) => {
t.name = Some(name);
Ok(t)
}
Err(e) => Err(e.in_template(name)),
}
}
}
#[derive(PartialEq, Clone, Debug)]
pub enum TemplateElement {
RawString(String),
Expression(Parameter),
HTMLExpression(Parameter),
HelperExpression(HelperTemplate),
HelperBlock(HelperTemplate),
DirectiveExpression(Directive),
DirectiveBlock(Directive),
PartialExpression(Directive),
PartialBlock(Directive),
Comment(String),
}
#[test]
fn test_parse_template() {
let source = "<h1>{{title}} ä½ å¥½</h1> {{{content}}}
{{#if date}}<p>good</p>{{else}}<p>bad</p>{{/if}}<img>{{foo bar}}ä¸æ–‡ä½ 好
{{#unless true}}kitkat{{^}}lollipop{{/unless}}";
let t = Template::compile(source.to_string()).ok().unwrap();
assert_eq!(t.elements.len(), 10);
assert_eq!(*t.elements.get(0).unwrap(), RawString("<h1>".to_string()));
assert_eq!(*t.elements.get(1).unwrap(),
Expression(Parameter::Name("title".to_string())));
assert_eq!(*t.elements.get(3).unwrap(),
HTMLExpression(Parameter::Name("content".to_string())));
match *t.elements.get(5).unwrap() {
HelperBlock(ref h) => {
assert_eq!(h.name, "if".to_string());
assert_eq!(h.params.len(), 1);
assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
}
_ => {
panic!("Helper expected here.");
}
};
match *t.elements.get(7).unwrap() {
HelperExpression(ref h) => {
assert_eq!(h.name, "foo".to_string());
assert_eq!(h.params.len(), 1);
assert_eq!(*(h.params.get(0).unwrap()), Parameter::Name("bar".into()));
}
_ => {
panic!("Helper expression here");
}
};
match *t.elements.get(9).unwrap() {
HelperBlock(ref h) => {
assert_eq!(h.name, "unless".to_string());
assert_eq!(h.params.len(), 1);
assert_eq!(h.inverse.as_ref().unwrap().elements.len(), 1);
}
_ => {
panic!("Helper expression here");
}
};
}
#[test]
fn test_parse_error() {
let source = "{{#ifequals name compare=\"hello\"}}\nhello\n\t{{else}}\ngood";
let t = Template::compile(source.to_string());
assert_eq!(t.unwrap_err(),
TemplateError::of(TemplateErrorReason::InvalidSyntax).at(4, 5));
}
#[test]
fn test_subexpression() {
let source = "{{foo (bar)}}{{foo (bar baz)}} hello {{#if (baz bar) then=(bar)}}world{{/if}}";
let t = Template::compile(source.to_string()).ok().unwrap();
assert_eq!(t.elements.len(), 4);
match *t.elements.get(0).unwrap() {
HelperExpression(ref h) => {
assert_eq!(h.name, "foo".to_owned());
assert_eq!(h.params.len(), 1);
if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
assert_eq!(t.name, "bar".to_owned());
} else {
panic!("Subexpression expected");
}
}
_ => {
panic!("Helper expression expected");
}
};
match *t.elements.get(1).unwrap() {
HelperExpression(ref h) => {
assert_eq!(h.name, "foo".to_string());
assert_eq!(h.params.len(), 1);
if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
assert_eq!(t.name, "bar".to_owned());
if let Some(&Parameter::Name(ref n)) = t.params.get(0) {
assert_eq!(n, "baz");
} else {
panic!("non-empty param expected ");
}
} else {
panic!("Subexpression expected");
}
}
_ => {
panic!("Helper expression expected");
}
};
match *t.elements.get(3).unwrap() {
HelperBlock(ref h) => {
assert_eq!(h.name, "if".to_string());
assert_eq!(h.params.len(), 1);
assert_eq!(h.hash.len(), 1);
if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
assert_eq!(t.name, "baz".to_owned());
if let Some(&Parameter::Name(ref n)) = t.params.get(0) {
assert_eq!(n, "bar");
} else {
panic!("non-empty param expected ");
}
} else {
panic!("Subexpression expected (baz bar)");
}
if let &Parameter::Subexpression(ref t) = h.hash.get("then").unwrap() {
assert_eq!(t.name, "bar".to_owned());
} else {
panic!("Subexpression expected (bar)");
}
}
_ => {
panic!("HelperBlock expected");
}
}
}
#[test]
fn test_white_space_omitter() {
let source = "hello~ {{~world~}} \n !{{~#if true}}else{{/if~}}".to_string();
let t = Template::compile(source).ok().unwrap();
assert_eq!(t.elements.len(), 4);
assert_eq!(t.elements[0], RawString("hello~".to_string()));
assert_eq!(t.elements[1], Expression(Parameter::Name("world".into())));
assert_eq!(t.elements[2], RawString("!".to_string()));
let t2 = Template::compile("{{#if true}}1 {{~ else ~}} 2 {{~/if}}".to_string()).ok().unwrap();
assert_eq!(t2.elements.len(), 1);
match t2.elements[0] {
HelperBlock(ref h) => {
assert_eq!(h.template.as_ref().unwrap().elements[0],
RawString("1".to_string()));
assert_eq!(h.inverse.as_ref().unwrap().elements[0],
RawString("2".to_string()));
}
_ => unreachable!(),
}
}
#[test]
fn test_unclosed_expression() {
let sources = ["{{invalid", "{{{invalid", "{{invalid}", "{{!hello"];
for s in sources.iter() {
let result = Template::compile(s.to_owned());
if let Err(e) = result {
match e.reason {
TemplateErrorReason::InvalidSyntax => {}
_ => {
panic!("Unexpected error type {}", e);
}
}
} else {
panic!("Undetected error");
}
}
}
#[test]
fn test_raw_helper() {
let source = "hello{{{{raw}}}}good{{night}}{{{{/raw}}}}world";
match Template::compile(source.to_owned()) {
Ok(t) => {
assert_eq!(t.elements.len(), 3);
assert_eq!(t.elements[0], RawString("hello".to_owned()));
assert_eq!(t.elements[2], RawString("world".to_owned()));
match t.elements[1] {
HelperBlock(ref h) => {
assert_eq!(h.name, "raw".to_owned());
if let Some(ref ht) = h.template {
assert_eq!(ht.elements.len(), 1);
assert_eq!(*ht.elements.get(0).unwrap(),
RawString("good{{night}}".to_owned()));
} else {
panic!("helper template not found");
}
}
_ => {
panic!("Unexpected element type");
}
}
}
Err(e) => {
panic!("{}", e);
}
}
}
#[test]
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
fn test_literal_parameter_parser() {
match Template::compile("{{hello 1 name=\"value\" valid=false ref=someref}}") {
Ok(t) => {
if let HelperExpression(ref ht) = t.elements[0] {
assert_eq!(ht.params[0], Parameter::Literal(Json::U64(1)));
assert_eq!(ht.hash["name"],
Parameter::Literal(Json::String("value".to_owned())));
assert_eq!(ht.hash["valid"], Parameter::Literal(Json::Boolean(false)));
assert_eq!(ht.hash["ref"], Parameter::Name("someref".to_owned()));
}
}
Err(e) => panic!("{}", e),
}
}
#[test]
#[cfg(serde_type)]
fn test_literal_parameter_parser() {
match Template::compile("{{hello 1 name=\"value\" valid=false ref=someref}}") {
Ok(t) => {
if let HelperExpression(ref ht) = t.elements[0] {
assert_eq!(ht.params[0], Parameter::Literal(Json::U64(1)));
assert_eq!(ht.hash["name"],
Parameter::Literal(Json::String("value".to_owned())));
assert_eq!(ht.hash["valid"], Parameter::Literal(Json::Bool(false)));
assert_eq!(ht.hash["ref"], Parameter::Name("someref".to_owned()));
}
}
Err(e) => panic!("{}", e),
}
}
#[test]
fn test_template_mapping() {
match Template::compile2("hello\n {{~world}}\n{{#if nice}}\n\thello\n{{/if}}", true) {
Ok(t) => {
if let Some(ref mapping) = t.mapping {
assert_eq!(mapping.len(), t.elements.len());
assert_eq!(mapping[0], TemplateMapping(1, 1));
assert_eq!(mapping[1], TemplateMapping(2, 3));
assert_eq!(mapping[3], TemplateMapping(3, 1));
} else {
panic!("should contains mapping");
}
}
Err(e) => panic!("{}", e),
}
}
#[test]
fn test_whitespace_elements() {
let c = Template::compile(" {{elem}}\n\t{{#if true}} \
{{/if}}\n{{{{raw}}}} {{{{/raw}}}}\n{{{{raw}}}}{{{{/raw}}}}\n");
assert_eq!(c.ok().unwrap().elements.len(), 9);
}
#[test]
fn test_block_param() {
match Template::compile("{{#each people as |person|}}{{person}}{{/each}}") {
Ok(t) => {
if let HelperBlock(ref ht) = t.elements[0] {
if let Some(BlockParam::Single(Parameter::Name(ref n))) = ht.block_param {
assert_eq!(n, "person");
} else {
panic!("block param expected.")
}
} else {
panic!("Helper block expected");
}
}
Err(e) => panic!("{}", e),
}
match Template::compile("{{#each people as |key val|}}{{person}}{{/each}}") {
Ok(t) => {
if let HelperBlock(ref ht) = t.elements[0] {
if let Some(BlockParam::Pair((Parameter::Name(ref n1), Parameter::Name(ref n2)))) =
ht.block_param {
assert_eq!(n1, "key");
assert_eq!(n2, "val");
} else {
panic!("helper block param expected.");
}
} else {
panic!("Helper block expected");
}
}
Err(e) => panic!("{}", e),
}
}
#[test]
#[cfg(not(feature="partial_legacy"))]
fn test_directive() {
match Template::compile("hello {{* ssh}} world") {
Err(e) => panic!("{}", e),
Ok(t) => {
if let DirectiveExpression(ref de) = t.elements[1] {
assert_eq!(de.name, Parameter::Name("ssh".to_owned()));
assert_eq!(de.template, None);
}
}
}
match Template::compile("hello {{> ssh}} world") {
Err(e) => panic!("{}", e),
Ok(t) => {
if let PartialExpression(ref de) = t.elements[1] {
assert_eq!(de.name, Parameter::Name("ssh".to_owned()));
assert_eq!(de.template, None);
}
}
}
match Template::compile("{{#*inline \"hello\"}}expand to hello{{/inline}}{{> hello}}") {
Err(e) => panic!("{}", e),
Ok(t) => {
if let DirectiveBlock(ref db) = t.elements[0] {
assert_eq!(db.name, Parameter::Name("inline".to_owned()));
assert_eq!(db.params[0],
Parameter::Literal(Json::String("hello".to_owned())));
assert_eq!(db.template.as_ref().unwrap().elements[0],
TemplateElement::RawString("expand to hello".to_owned()));
}
}
}
match Template::compile("{{#> layout \"hello\"}}expand to hello{{/layout}}{{> hello}}") {
Err(e) => panic!("{}", e),
Ok(t) => {
if let PartialBlock(ref db) = t.elements[0] {
assert_eq!(db.name, Parameter::Name("layout".to_owned()));
assert_eq!(db.params[0],
Parameter::Literal(Json::String("hello".to_owned())));
assert_eq!(db.template.as_ref().unwrap().elements[0],
TemplateElement::RawString("expand to hello".to_owned()));
}
}
}
}