]> git.proxmox.com Git - rustc.git/blob - src/vendor/handlebars/src/template.rs
New upstream version 1.20.0+dfsg1
[rustc.git] / src / vendor / handlebars / src / template.rs
1 use std::slice::Iter;
2 use std::iter::Peekable;
3 use std::convert::From;
4 use std::collections::{BTreeMap, VecDeque};
5 use pest::prelude::*;
6
7 use serde_json::value::Value as Json;
8 use std::str::FromStr;
9
10 use grammar::{Rdp, Rule};
11
12 use error::{TemplateError, TemplateErrorReason};
13
14 use self::TemplateElement::*;
15
16 #[derive(PartialEq, Clone, Debug)]
17 pub struct TemplateMapping(pub usize, pub usize);
18
19 /// A handlebars template
20 #[derive(PartialEq, Clone, Debug)]
21 pub struct Template {
22 pub name: Option<String>,
23 pub elements: Vec<TemplateElement>,
24 pub mapping: Option<Vec<TemplateMapping>>,
25 }
26
27 #[derive(PartialEq, Clone, Debug)]
28 pub struct Subexpression {
29 pub name: String,
30 pub params: Vec<Parameter>,
31 pub hash: BTreeMap<String, Parameter>,
32 }
33
34 impl Subexpression {
35 pub fn is_helper(&self) -> bool {
36 !(self.params.is_empty() && self.hash.is_empty())
37 }
38
39 pub fn as_template(&self) -> Template {
40 let mut t = Template::new(false);
41 let el = if self.is_helper() {
42 HelperExpression(HelperTemplate::from(self))
43 } else {
44 Expression(Parameter::Name(self.name.clone()))
45 };
46 t.elements.push(el);
47 t
48 }
49 }
50
51 #[derive(PartialEq, Clone, Debug)]
52 pub enum BlockParam {
53 Single(Parameter),
54 Pair((Parameter, Parameter)),
55 }
56
57 #[derive(PartialEq, Clone, Debug)]
58 pub struct ExpressionSpec {
59 pub name: Parameter,
60 pub params: Vec<Parameter>,
61 pub hash: BTreeMap<String, Parameter>,
62 pub block_param: Option<BlockParam>,
63 pub omit_pre_ws: bool,
64 pub omit_pro_ws: bool,
65 }
66
67 #[derive(PartialEq, Clone, Debug)]
68 pub enum Parameter {
69 Name(String),
70 Literal(Json),
71 Subexpression(Subexpression),
72 }
73
74 #[derive(PartialEq, Clone, Debug)]
75 pub struct HelperTemplate {
76 pub name: String,
77 pub params: Vec<Parameter>,
78 pub hash: BTreeMap<String, Parameter>,
79 pub block_param: Option<BlockParam>,
80 pub template: Option<Template>,
81 pub inverse: Option<Template>,
82 pub block: bool,
83 }
84
85 impl<'a> From<&'a Subexpression> for HelperTemplate {
86 fn from(s: &Subexpression) -> HelperTemplate {
87 HelperTemplate {
88 name: s.name.clone(),
89 params: s.params.clone(),
90 hash: s.hash.clone(),
91 block_param: None,
92 template: None,
93 inverse: None,
94 block: false,
95 }
96 }
97 }
98
99 #[derive(PartialEq, Clone, Debug)]
100 pub struct Directive {
101 pub name: Parameter,
102 pub params: Vec<Parameter>,
103 pub hash: BTreeMap<String, Parameter>,
104 pub template: Option<Template>,
105 }
106
107 impl Parameter {
108 pub fn as_name(self) -> Option<String> {
109 if let Parameter::Name(n) = self {
110 Some(n)
111 } else {
112 None
113 }
114 }
115
116 pub fn parse(s: &str) -> Result<Parameter, TemplateError> {
117 let mut parser = Rdp::new(StringInput::new(s));
118 if !parser.parameter() {
119 return Err(TemplateError::of(TemplateErrorReason::InvalidParam(s.to_owned())));
120 }
121
122 let mut it = parser.queue().iter().peekable();
123 Template::parse_param(s, &mut it, s.len() - 1)
124 }
125 }
126
127 impl Template {
128 pub fn new(mapping: bool) -> Template {
129 Template {
130 elements: Vec::new(),
131 name: None,
132 mapping: if mapping { Some(Vec::new()) } else { None },
133 }
134 }
135
136 fn push_element(&mut self, e: TemplateElement, line: usize, col: usize) {
137 self.elements.push(e);
138 if let Some(ref mut maps) = self.mapping {
139 maps.push(TemplateMapping(line, col));
140 }
141 }
142
143 pub fn compile<S: AsRef<str>>(source: S) -> Result<Template, TemplateError> {
144 Template::compile2(source, false)
145 }
146
147 #[inline]
148 fn parse_subexpression<'a>(source: &'a str,
149 it: &mut Peekable<Iter<Token<Rule>>>,
150 limit: usize)
151 -> Result<Parameter, TemplateError> {
152 let espec = try!(Template::parse_expression(source, it.by_ref(), limit));
153 if let Parameter::Name(name) = espec.name {
154 Ok(Parameter::Subexpression(Subexpression {
155 name: name,
156 params: espec.params,
157 hash: espec.hash,
158 }))
159 } else {
160 // line/col no
161 Err(TemplateError::of(TemplateErrorReason::NestedSubexpression))
162 }
163 }
164
165 #[inline]
166 fn parse_name<'a>(source: &'a str,
167 it: &mut Peekable<Iter<Token<Rule>>>,
168 _: usize)
169 -> Result<Parameter, TemplateError> {
170 let name_node = it.next().unwrap();
171 match name_node.rule {
172 Rule::identifier |
173 Rule::reference |
174 Rule::invert_tag_item => {
175 Ok(Parameter::Name(source[name_node.start..name_node.end].to_owned()))
176 }
177 Rule::subexpression => {
178 Template::parse_subexpression(source, it.by_ref(), name_node.end)
179 }
180 _ => unreachable!(),
181 }
182 }
183
184 #[inline]
185 fn parse_param<'a>(source: &'a str,
186 it: &mut Peekable<Iter<Token<Rule>>>,
187 _: usize)
188 -> Result<Parameter, TemplateError> {
189 let mut param = it.next().unwrap();
190 if param.rule == Rule::param {
191 param = it.next().unwrap();
192 }
193 let result = match param.rule {
194 Rule::reference => Parameter::Name(source[param.start..param.end].to_owned()),
195 Rule::literal => {
196 let s = &source[param.start..param.end];
197 if let Ok(json) = Json::from_str(s) {
198 Parameter::Literal(json)
199 } else {
200 Parameter::Name(s.to_owned())
201 }
202 }
203 Rule::subexpression => {
204 try!(Template::parse_subexpression(source, it.by_ref(), param.end))
205 }
206 _ => unreachable!(),
207 };
208
209 loop {
210 if let Some(ref n) = it.peek() {
211 if n.end > param.end {
212 break;
213 }
214 } else {
215 break;
216 }
217
218 it.next();
219 }
220
221 Ok(result)
222 }
223
224 #[inline]
225 fn parse_hash<'a>(source: &'a str,
226 it: &mut Peekable<Iter<Token<Rule>>>,
227 limit: usize)
228 -> Result<(String, Parameter), TemplateError> {
229 let name = it.next().unwrap();
230 // identifier
231 let key = source[name.start..name.end].to_owned();
232
233 let value = try!(Template::parse_param(source, it.by_ref(), limit));
234 Ok((key, value))
235 }
236
237 #[inline]
238 fn parse_block_param<'a>(source: &'a str,
239 it: &mut Peekable<Iter<Token<Rule>>>,
240 limit: usize)
241 -> Result<BlockParam, TemplateError> {
242 let p1_name = it.next().unwrap();
243 // identifier
244 let p1 = source[p1_name.start..p1_name.end].to_owned();
245
246 let p2 = it.peek().and_then(|p2_name| if p2_name.end <= limit {
247 Some(source[p2_name.start..p2_name.end].to_owned())
248 } else {
249 None
250 });
251
252 if p2.is_some() {
253 it.next();
254 Ok(BlockParam::Pair((Parameter::Name(p1), Parameter::Name(p2.unwrap()))))
255 } else {
256 Ok(BlockParam::Single(Parameter::Name(p1)))
257 }
258 }
259
260 #[inline]
261 fn parse_expression<'a>(source: &'a str,
262 it: &mut Peekable<Iter<Token<Rule>>>,
263 limit: usize)
264 -> Result<ExpressionSpec, TemplateError> {
265 let mut params: Vec<Parameter> = Vec::new();
266 let mut hashes: BTreeMap<String, Parameter> = BTreeMap::new();
267 let mut omit_pre_ws = false;
268 let mut omit_pro_ws = false;
269 let mut block_param = None;
270
271 if it.peek().unwrap().rule == Rule::pre_whitespace_omitter {
272 omit_pre_ws = true;
273 it.next();
274 }
275
276 let name = try!(Template::parse_name(source, it.by_ref(), limit));
277
278 loop {
279 let rule;
280 let end;
281 if let Some(ref token) = it.peek() {
282 if token.end < limit {
283 rule = token.rule;
284 end = token.end;
285 } else {
286 break;
287 }
288 } else {
289 break;
290 }
291
292 it.next();
293
294 match rule {
295 Rule::param => {
296 params.push(try!(Template::parse_param(source, it.by_ref(), end)));
297 }
298 Rule::hash => {
299 let (key, value) = try!(Template::parse_hash(source, it.by_ref(), end));
300 hashes.insert(key, value);
301 }
302 Rule::block_param => {
303 block_param = Some(try!(Template::parse_block_param(source, it.by_ref(), end)));
304 }
305 Rule::pro_whitespace_omitter => {
306 omit_pro_ws = true;
307 }
308 _ => {}
309 }
310 }
311 Ok(ExpressionSpec {
312 name: name,
313 params: params,
314 hash: hashes,
315 block_param: block_param,
316 omit_pre_ws: omit_pre_ws,
317 omit_pro_ws: omit_pro_ws,
318 })
319 }
320
321 #[inline]
322 fn remove_previous_whitespace(template_stack: &mut VecDeque<Template>) {
323 let mut t = template_stack.front_mut().unwrap();
324 if let Some(el) = t.elements.pop() {
325 if let RawString(ref text) = el {
326 t.elements.push(RawString(text.trim_right().to_owned()));
327 } else {
328 t.elements.push(el);
329 }
330 }
331 }
332
333 pub fn compile2<S: AsRef<str>>(source: S, mapping: bool) -> Result<Template, TemplateError> {
334 let source = source.as_ref();
335 let mut helper_stack: VecDeque<HelperTemplate> = VecDeque::new();
336 let mut directive_stack: VecDeque<Directive> = VecDeque::new();
337 let mut template_stack: VecDeque<Template> = VecDeque::new();
338
339 let mut omit_pro_ws = false;
340
341 let input = StringInput::new(source);
342 let mut parser = Rdp::new(input);
343
344 if !parser.handlebars() {
345 let (_, pos) = parser.expected();
346 let (line_no, col_no) = parser.input().line_col(pos);
347 return Err(TemplateError::of(TemplateErrorReason::InvalidSyntax).at(line_no, col_no));
348 }
349
350 let mut it = parser.queue().iter().peekable();
351 let mut prev_end = 0;
352 loop {
353 if let Some(ref token) = it.next() {
354
355 if token.rule != Rule::template {
356 if token.start != prev_end && !omit_pro_ws && token.rule != Rule::raw_text &&
357 token.rule != Rule::raw_block_text {
358 let (line_no, col_no) = parser.input().line_col(prev_end);
359 if token.rule == Rule::raw_block_end {
360 let text = &source[prev_end..token.start];
361 let mut t = Template::new(mapping);
362 t.push_element(RawString(text.to_owned()), line_no, col_no);
363 template_stack.push_front(t);
364 } else {
365 let text = &source[prev_end..token.start];
366 let mut t = template_stack.front_mut().unwrap();
367 t.push_element(RawString(text.to_owned()), line_no, col_no);
368 }
369 }
370 }
371
372 let (line_no, col_no) = parser.input().line_col(token.start);
373 match token.rule {
374 Rule::template => {
375 template_stack.push_front(Template::new(mapping));
376 }
377 Rule::raw_text => {
378 let mut text = &source[prev_end..token.end];
379 if omit_pro_ws {
380 text = text.trim_left();
381 }
382 let mut t = template_stack.front_mut().unwrap();
383 t.push_element(RawString(text.to_owned()), line_no, col_no);
384 }
385 Rule::helper_block_start |
386 Rule::raw_block_start |
387 Rule::directive_block_start |
388 Rule::partial_block_start => {
389 let exp = try!(Template::parse_expression(source, it.by_ref(), token.end));
390
391 match token.rule {
392 Rule::helper_block_start |
393 Rule::raw_block_start => {
394 let helper_template = HelperTemplate {
395 name: exp.name.as_name().unwrap(),
396 params: exp.params,
397 hash: exp.hash,
398 block_param: exp.block_param,
399 block: true,
400 template: None,
401 inverse: None,
402 };
403 helper_stack.push_front(helper_template);
404 }
405 Rule::directive_block_start |
406 Rule::partial_block_start => {
407 let directive = Directive {
408 name: exp.name,
409 params: exp.params,
410 hash: exp.hash,
411 template: None,
412 };
413 directive_stack.push_front(directive);
414 }
415 _ => unreachable!(),
416 }
417
418 if exp.omit_pre_ws {
419 Template::remove_previous_whitespace(&mut template_stack);
420 }
421 omit_pro_ws = exp.omit_pro_ws;
422
423 let mut t = template_stack.front_mut().unwrap();
424 if let Some(ref mut maps) = t.mapping {
425 maps.push(TemplateMapping(line_no, col_no));
426 }
427 }
428 Rule::invert_tag => {
429 // hack: invert_tag structure is similar to ExpressionSpec, so I
430 // use it here to represent the data
431 let exp = try!(Template::parse_expression(source, it.by_ref(), token.end));
432
433 if exp.omit_pre_ws {
434 Template::remove_previous_whitespace(&mut template_stack);
435 }
436 omit_pro_ws = exp.omit_pro_ws;
437
438 let t = template_stack.pop_front().unwrap();
439 let mut h = helper_stack.front_mut().unwrap();
440 h.template = Some(t);
441 }
442 Rule::raw_block_text => {
443 let mut text = &source[prev_end..token.end];
444 if omit_pro_ws {
445 text = text.trim_left();
446 }
447 let mut t = Template::new(mapping);
448 t.push_element(RawString(text.to_owned()), line_no, col_no);
449 template_stack.push_front(t);
450 }
451 Rule::expression |
452 Rule::html_expression |
453 Rule::helper_expression |
454 Rule::directive_expression |
455 Rule::partial_expression |
456 Rule::helper_block_end |
457 Rule::raw_block_end |
458 Rule::directive_block_end |
459 Rule::partial_block_end => {
460 let exp = try!(Template::parse_expression(source, it.by_ref(), token.end));
461 if exp.omit_pre_ws {
462 Template::remove_previous_whitespace(&mut template_stack);
463 }
464
465 omit_pro_ws = exp.omit_pro_ws;
466
467 match token.rule {
468 Rule::expression => {
469 let el = Expression(exp.name);
470 let mut t = template_stack.front_mut().unwrap();
471 t.push_element(el, line_no, col_no);
472 }
473 Rule::html_expression => {
474 let el = HTMLExpression(exp.name);
475 let mut t = template_stack.front_mut().unwrap();
476 t.push_element(el, line_no, col_no);
477 }
478 Rule::helper_expression => {
479 let helper_template = HelperTemplate {
480 name: exp.name.as_name().unwrap(),
481 params: exp.params,
482 hash: exp.hash,
483 block_param: exp.block_param,
484 block: false,
485 template: None,
486 inverse: None,
487 };
488 let el = HelperExpression(helper_template);
489 let mut t = template_stack.front_mut().unwrap();
490 t.push_element(el, line_no, col_no);
491 }
492 Rule::directive_expression |
493 Rule::partial_expression => {
494 let directive = Directive {
495 name: exp.name,
496 params: exp.params,
497 hash: exp.hash,
498 template: None,
499 };
500 let el = if token.rule == Rule::directive_expression {
501 DirectiveExpression(directive)
502 } else {
503 PartialExpression(directive)
504 };
505 let mut t = template_stack.front_mut().unwrap();
506 t.push_element(el, line_no, col_no);
507 }
508 Rule::helper_block_end |
509 Rule::raw_block_end => {
510 let mut h = helper_stack.pop_front().unwrap();
511 let close_tag_name = exp.name.as_name().unwrap();
512 if h.name == close_tag_name {
513 let prev_t = template_stack.pop_front().unwrap();
514 if h.template.is_some() {
515 h.inverse = Some(prev_t);
516 } else {
517 h.template = Some(prev_t);
518 }
519 let t = template_stack.front_mut().unwrap();
520 t.elements.push(HelperBlock(h));
521 } else {
522 return Err(TemplateError::of(
523 TemplateErrorReason::MismatchingClosedHelper(
524 h.name, close_tag_name)).at(line_no, col_no));
525 }
526 }
527 Rule::directive_block_end |
528 Rule::partial_block_end => {
529 let mut d = directive_stack.pop_front().unwrap();
530 let close_tag_name = exp.name;
531 if d.name == close_tag_name {
532 let prev_t = template_stack.pop_front().unwrap();
533 d.template = Some(prev_t);
534 let t = template_stack.front_mut().unwrap();
535 if token.rule == Rule::directive_block_end {
536 t.elements.push(DirectiveBlock(d));
537 } else {
538 t.elements.push(PartialBlock(d));
539 }
540 } else {
541 return Err(TemplateError::of(
542 TemplateErrorReason::MismatchingClosedDirective(
543 d.name, close_tag_name)).at(line_no, col_no));
544 }
545 }
546 _ => unreachable!(),
547 }
548 }
549 Rule::hbs_comment => {
550 let text = parser.input().slice(token.start + 3, token.end - 2);
551 let mut t = template_stack.front_mut().unwrap();
552 t.push_element(Comment(text.to_owned()), line_no, col_no);
553 }
554 _ => {}
555 }
556
557 if token.rule != Rule::template {
558 prev_end = token.end;
559 }
560 } else {
561 if prev_end < source.len() {
562 let text = &source[prev_end..source.len()];
563 let (line_no, col_no) = parser.input().line_col(prev_end);
564 let mut t = template_stack.front_mut().unwrap();
565 t.push_element(RawString(text.to_owned()), line_no, col_no);
566 }
567 return Ok(template_stack.pop_front().unwrap());
568 }
569 }
570 }
571
572 pub fn compile_with_name<S: AsRef<str>>(source: S,
573 name: String,
574 mapping: bool)
575 -> Result<Template, TemplateError> {
576 match Template::compile2(source, mapping) {
577 Ok(mut t) => {
578 t.name = Some(name);
579 Ok(t)
580 }
581 Err(e) => Err(e.in_template(name)),
582 }
583 }
584 }
585
586 #[derive(PartialEq, Clone, Debug)]
587 pub enum TemplateElement {
588 RawString(String),
589 Expression(Parameter),
590 HTMLExpression(Parameter),
591 HelperExpression(HelperTemplate),
592 HelperBlock(HelperTemplate),
593 DirectiveExpression(Directive),
594 DirectiveBlock(Directive),
595 PartialExpression(Directive),
596 PartialBlock(Directive),
597 Comment(String),
598 }
599
600 #[test]
601 fn test_parse_template() {
602 let source = "<h1>{{title}} 你好</h1> {{{content}}}
603 {{#if date}}<p>good</p>{{else}}<p>bad</p>{{/if}}<img>{{foo bar}}中文你好
604 {{#unless true}}kitkat{{^}}lollipop{{/unless}}";
605 let t = Template::compile(source.to_string()).ok().unwrap();
606
607 assert_eq!(t.elements.len(), 10);
608
609 assert_eq!(*t.elements.get(0).unwrap(), RawString("<h1>".to_string()));
610 assert_eq!(*t.elements.get(1).unwrap(),
611 Expression(Parameter::Name("title".to_string())));
612
613 assert_eq!(*t.elements.get(3).unwrap(),
614 HTMLExpression(Parameter::Name("content".to_string())));
615
616 match *t.elements.get(5).unwrap() {
617 HelperBlock(ref h) => {
618 assert_eq!(h.name, "if".to_string());
619 assert_eq!(h.params.len(), 1);
620 assert_eq!(h.template
621 .as_ref()
622 .unwrap()
623 .elements
624 .len(),
625 1);
626 }
627 _ => {
628 panic!("Helper expected here.");
629 }
630 };
631
632 match *t.elements.get(7).unwrap() {
633 HelperExpression(ref h) => {
634 assert_eq!(h.name, "foo".to_string());
635 assert_eq!(h.params.len(), 1);
636 assert_eq!(*(h.params.get(0).unwrap()), Parameter::Name("bar".into()));
637 }
638 _ => {
639 panic!("Helper expression here");
640 }
641 };
642
643 match *t.elements.get(9).unwrap() {
644 HelperBlock(ref h) => {
645 assert_eq!(h.name, "unless".to_string());
646 assert_eq!(h.params.len(), 1);
647 assert_eq!(h.inverse
648 .as_ref()
649 .unwrap()
650 .elements
651 .len(),
652 1);
653 }
654 _ => {
655 panic!("Helper expression here");
656 }
657 };
658
659 }
660
661 #[test]
662 fn test_parse_error() {
663 let source = "{{#ifequals name compare=\"hello\"}}\nhello\n\t{{else}}\ngood";
664
665 let t = Template::compile(source.to_string());
666
667 assert_eq!(t.unwrap_err(),
668 TemplateError::of(TemplateErrorReason::InvalidSyntax).at(4, 5));
669 }
670
671 #[test]
672 fn test_subexpression() {
673 let source = "{{foo (bar)}}{{foo (bar baz)}} hello {{#if (baz bar) then=(bar)}}world{{/if}}";
674 let t = Template::compile(source.to_string()).ok().unwrap();
675
676 assert_eq!(t.elements.len(), 4);
677 match *t.elements.get(0).unwrap() {
678 HelperExpression(ref h) => {
679 assert_eq!(h.name, "foo".to_owned());
680 assert_eq!(h.params.len(), 1);
681 if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
682 assert_eq!(t.name, "bar".to_owned());
683 } else {
684 panic!("Subexpression expected");
685 }
686 }
687 _ => {
688 panic!("Helper expression expected");
689 }
690 };
691
692 match *t.elements.get(1).unwrap() {
693 HelperExpression(ref h) => {
694 assert_eq!(h.name, "foo".to_string());
695 assert_eq!(h.params.len(), 1);
696 if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
697 assert_eq!(t.name, "bar".to_owned());
698 if let Some(&Parameter::Name(ref n)) = t.params.get(0) {
699 assert_eq!(n, "baz");
700 } else {
701 panic!("non-empty param expected ");
702 }
703 } else {
704 panic!("Subexpression expected");
705 }
706 }
707 _ => {
708 panic!("Helper expression expected");
709 }
710 };
711
712 match *t.elements.get(3).unwrap() {
713 HelperBlock(ref h) => {
714 assert_eq!(h.name, "if".to_string());
715 assert_eq!(h.params.len(), 1);
716 assert_eq!(h.hash.len(), 1);
717
718 if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
719 assert_eq!(t.name, "baz".to_owned());
720 if let Some(&Parameter::Name(ref n)) = t.params.get(0) {
721 assert_eq!(n, "bar");
722 } else {
723 panic!("non-empty param expected ");
724 }
725
726 } else {
727 panic!("Subexpression expected (baz bar)");
728 }
729
730 if let &Parameter::Subexpression(ref t) = h.hash.get("then").unwrap() {
731 assert_eq!(t.name, "bar".to_owned());
732 } else {
733 panic!("Subexpression expected (bar)");
734 }
735 }
736 _ => {
737 panic!("HelperBlock expected");
738 }
739 }
740 }
741
742 #[test]
743 fn test_white_space_omitter() {
744 let source = "hello~ {{~world~}} \n !{{~#if true}}else{{/if~}}".to_string();
745 let t = Template::compile(source).ok().unwrap();
746
747 assert_eq!(t.elements.len(), 4);
748
749 assert_eq!(t.elements[0], RawString("hello~".to_string()));
750 assert_eq!(t.elements[1], Expression(Parameter::Name("world".into())));
751 assert_eq!(t.elements[2], RawString("!".to_string()));
752
753 let t2 = Template::compile("{{#if true}}1 {{~ else ~}} 2 {{~/if}}".to_string()).ok().unwrap();
754 assert_eq!(t2.elements.len(), 1);
755 match t2.elements[0] {
756 HelperBlock(ref h) => {
757 assert_eq!(h.template
758 .as_ref()
759 .unwrap()
760 .elements
761 [0],
762 RawString("1".to_string()));
763 assert_eq!(h.inverse
764 .as_ref()
765 .unwrap()
766 .elements
767 [0],
768 RawString("2".to_string()));
769 }
770 _ => unreachable!(),
771 }
772 }
773
774 #[test]
775 fn test_unclosed_expression() {
776 let sources = ["{{invalid", "{{{invalid", "{{invalid}", "{{!hello"];
777 for s in sources.iter() {
778 let result = Template::compile(s.to_owned());
779 if let Err(e) = result {
780 match e.reason {
781 TemplateErrorReason::InvalidSyntax => {}
782 _ => {
783 panic!("Unexpected error type {}", e);
784 }
785 }
786 } else {
787 panic!("Undetected error");
788 }
789 }
790 }
791
792 #[test]
793 fn test_raw_helper() {
794 let source = "hello{{{{raw}}}}good{{night}}{{{{/raw}}}}world";
795 match Template::compile(source.to_owned()) {
796 Ok(t) => {
797 assert_eq!(t.elements.len(), 3);
798 assert_eq!(t.elements[0], RawString("hello".to_owned()));
799 assert_eq!(t.elements[2], RawString("world".to_owned()));
800 match t.elements[1] {
801 HelperBlock(ref h) => {
802 assert_eq!(h.name, "raw".to_owned());
803 if let Some(ref ht) = h.template {
804 assert_eq!(ht.elements.len(), 1);
805 assert_eq!(*ht.elements.get(0).unwrap(),
806 RawString("good{{night}}".to_owned()));
807 } else {
808 panic!("helper template not found");
809 }
810 }
811 _ => {
812 panic!("Unexpected element type");
813 }
814 }
815 }
816 Err(e) => {
817 panic!("{}", e);
818 }
819
820 }
821 }
822
823 #[test]
824 #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
825 fn test_literal_parameter_parser() {
826 match Template::compile("{{hello 1 name=\"value\" valid=false ref=someref}}") {
827 Ok(t) => {
828 if let HelperExpression(ref ht) = t.elements[0] {
829 assert_eq!(ht.params[0], Parameter::Literal(Json::U64(1)));
830 assert_eq!(ht.hash["name"],
831 Parameter::Literal(Json::String("value".to_owned())));
832 assert_eq!(ht.hash["valid"], Parameter::Literal(Json::Boolean(false)));
833 assert_eq!(ht.hash["ref"], Parameter::Name("someref".to_owned()));
834 }
835 }
836 Err(e) => panic!("{}", e),
837 }
838 }
839
840 #[test]
841 #[cfg(serde_type)]
842 fn test_literal_parameter_parser() {
843 match Template::compile("{{hello 1 name=\"value\" valid=false ref=someref}}") {
844 Ok(t) => {
845 if let HelperExpression(ref ht) = t.elements[0] {
846 assert_eq!(ht.params[0], Parameter::Literal(Json::U64(1)));
847 assert_eq!(ht.hash["name"],
848 Parameter::Literal(Json::String("value".to_owned())));
849 assert_eq!(ht.hash["valid"], Parameter::Literal(Json::Bool(false)));
850 assert_eq!(ht.hash["ref"], Parameter::Name("someref".to_owned()));
851 }
852 }
853 Err(e) => panic!("{}", e),
854 }
855 }
856
857 #[test]
858 fn test_template_mapping() {
859 match Template::compile2("hello\n {{~world}}\n{{#if nice}}\n\thello\n{{/if}}", true) {
860 Ok(t) => {
861 if let Some(ref mapping) = t.mapping {
862 assert_eq!(mapping.len(), t.elements.len());
863 assert_eq!(mapping[0], TemplateMapping(1, 1));
864 assert_eq!(mapping[1], TemplateMapping(2, 3));
865 assert_eq!(mapping[3], TemplateMapping(3, 1));
866 } else {
867 panic!("should contains mapping");
868 }
869 }
870 Err(e) => panic!("{}", e),
871 }
872 }
873
874 #[test]
875 fn test_whitespace_elements() {
876 let c = Template::compile(" {{elem}}\n\t{{#if true}} \
877 {{/if}}\n{{{{raw}}}} {{{{/raw}}}}\n{{{{raw}}}}{{{{/raw}}}}\n");
878 assert_eq!(c.ok()
879 .unwrap()
880 .elements
881 .len(),
882 9);
883 }
884
885 #[test]
886 fn test_block_param() {
887 match Template::compile("{{#each people as |person|}}{{person}}{{/each}}") {
888 Ok(t) => {
889 if let HelperBlock(ref ht) = t.elements[0] {
890 if let Some(BlockParam::Single(Parameter::Name(ref n))) = ht.block_param {
891 assert_eq!(n, "person");
892 } else {
893 panic!("block param expected.")
894 }
895 } else {
896 panic!("Helper block expected");
897 }
898 }
899 Err(e) => panic!("{}", e),
900 }
901
902 match Template::compile("{{#each people as |key val|}}{{person}}{{/each}}") {
903 Ok(t) => {
904 if let HelperBlock(ref ht) = t.elements[0] {
905 if let Some(BlockParam::Pair((Parameter::Name(ref n1),
906 Parameter::Name(ref n2)))) = ht.block_param {
907 assert_eq!(n1, "key");
908 assert_eq!(n2, "val");
909 } else {
910 panic!("helper block param expected.");
911 }
912 } else {
913 panic!("Helper block expected");
914 }
915 }
916 Err(e) => panic!("{}", e),
917 }
918 }
919
920 #[test]
921 #[cfg(not(feature="partial_legacy"))]
922 fn test_directive() {
923 match Template::compile("hello {{* ssh}} world") {
924 Err(e) => panic!("{}", e),
925 Ok(t) => {
926 if let DirectiveExpression(ref de) = t.elements[1] {
927 assert_eq!(de.name, Parameter::Name("ssh".to_owned()));
928 assert_eq!(de.template, None);
929 }
930 }
931 }
932
933 match Template::compile("hello {{> ssh}} world") {
934 Err(e) => panic!("{}", e),
935 Ok(t) => {
936 if let PartialExpression(ref de) = t.elements[1] {
937 assert_eq!(de.name, Parameter::Name("ssh".to_owned()));
938 assert_eq!(de.template, None);
939 }
940 }
941 }
942
943 match Template::compile("{{#*inline \"hello\"}}expand to hello{{/inline}}{{> hello}}") {
944 Err(e) => panic!("{}", e),
945 Ok(t) => {
946 if let DirectiveBlock(ref db) = t.elements[0] {
947 assert_eq!(db.name, Parameter::Name("inline".to_owned()));
948 assert_eq!(db.params[0],
949 Parameter::Literal(Json::String("hello".to_owned())));
950 assert_eq!(db.template
951 .as_ref()
952 .unwrap()
953 .elements
954 [0],
955 TemplateElement::RawString("expand to hello".to_owned()));
956 }
957 }
958 }
959
960 match Template::compile("{{#> layout \"hello\"}}expand to hello{{/layout}}{{> hello}}") {
961 Err(e) => panic!("{}", e),
962 Ok(t) => {
963 if let PartialBlock(ref db) = t.elements[0] {
964 assert_eq!(db.name, Parameter::Name("layout".to_owned()));
965 assert_eq!(db.params[0],
966 Parameter::Literal(Json::String("hello".to_owned())));
967 assert_eq!(db.template
968 .as_ref()
969 .unwrap()
970 .elements
971 [0],
972 TemplateElement::RawString("expand to hello".to_owned()));
973 }
974 }
975 }
976 }