1 use std::collections::{HashMap, BTreeMap, VecDeque};
2 use std::error;
3 use std::fmt;
4 use std::io::Write;
5 use std::io::Error as IOError;
6
7 #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
8 use serialize::json::{ToJson, Json};
9 #[cfg(feature = "serde_type")]
10 use serde_json::value::Value as Json;
11 #[cfg(feature = "serde_type")]
12 use serde::ser::Serialize as ToJson;
13
14 use template::{Template, TemplateElement, Parameter, HelperTemplate, TemplateMapping, BlockParam,
15 Directive as DirectiveTemplate};
16 use template::TemplateElement::*;
17 use registry::Registry;
18 use context::{Context, JsonRender};
19 use support::str::StringWriter;
20 #[cfg(feature="partial4")]
21 use partial;
22
23 #[derive(Debug, Clone)]
24 pub struct RenderError {
25 pub desc: String,
26 pub template_name: Option<String>,
27 pub line_no: Option<usize>,
28 pub column_no: Option<usize>,
29 }
30
31 impl fmt::Display for RenderError {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>32 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
33 match (self.line_no, self.column_no) {
34 (Some(line), Some(col)) => {
35 write!(f,
36 "{} at {} line {}, col {}",
37 self.desc,
38 self.template_name.as_ref().unwrap_or(&"Unnamed template".to_owned()),
39 line,
40 col)
41 }
42 _ => write!(f, "{}", self.desc),
43 }
44
45 }
46 }
47
48 impl error::Error for RenderError {
description(&self) -> &str49 fn description(&self) -> &str {
50 &self.desc[..]
51 }
52 }
53
54 impl From<IOError> for RenderError {
from(_: IOError) -> RenderError55 fn from(_: IOError) -> RenderError {
56 RenderError::new("IO Error")
57 }
58 }
59
60 impl RenderError {
new<T: AsRef<str>>(desc: T) -> RenderError61 pub fn new<T: AsRef<str>>(desc: T) -> RenderError {
62 RenderError {
63 desc: desc.as_ref().to_owned(),
64 template_name: None,
65 line_no: None,
66 column_no: None,
67 }
68 }
69 }
70
71 /// The context of a render call
72 ///
73 /// this context stores information of a render and a writer where generated
74 /// content is written to.
75 ///
76 pub struct RenderContext<'a> {
77 partials: HashMap<String, Template>,
78 path: String,
79 local_path_root: VecDeque<String>,
80 local_variables: HashMap<String, Json>,
81 default_var: Json,
82 block_context: VecDeque<Context>,
83 /// the `Write` where page is generated
84 pub writer: &'a mut Write,
85 /// current template name
86 pub current_template: Option<String>,
87 /// root template name
88 pub root_template: Option<String>,
89 pub disable_escape: bool,
90 }
91
92 impl<'a> RenderContext<'a> {
93 /// Create a render context from a `Write`
new(w: &'a mut Write) -> RenderContext<'a>94 pub fn new(w: &'a mut Write) -> RenderContext<'a> {
95 RenderContext {
96 partials: HashMap::new(),
97 path: ".".to_string(),
98 local_path_root: VecDeque::new(),
99 local_variables: HashMap::new(),
100 default_var: Json::Null,
101 block_context: VecDeque::new(),
102 writer: w,
103 current_template: None,
104 root_template: None,
105 disable_escape: false,
106 }
107 }
108
109 /// Create a new `RenderContext` with a different `Write`
with_writer<'b>(&self, w: &'b mut Write) -> RenderContext<'b>110 pub fn with_writer<'b>(&self, w: &'b mut Write) -> RenderContext<'b> {
111 RenderContext {
112 partials: self.partials.clone(),
113 path: self.path.clone(),
114 local_path_root: self.local_path_root.clone(),
115 local_variables: self.local_variables.clone(),
116 default_var: self.default_var.clone(),
117 block_context: self.block_context.clone(),
118 writer: w,
119 current_template: self.current_template.clone(),
120 root_template: self.root_template.clone(),
121 disable_escape: self.disable_escape,
122 }
123 }
124
derive(&mut self) -> RenderContext125 pub fn derive(&mut self) -> RenderContext {
126 RenderContext {
127 partials: self.partials.clone(),
128 path: self.path.clone(),
129 local_path_root: self.local_path_root.clone(),
130 local_variables: self.local_variables.clone(),
131 default_var: self.default_var.clone(),
132 block_context: self.block_context.clone(),
133 writer: self.writer,
134 current_template: self.current_template.clone(),
135 root_template: self.root_template.clone(),
136 disable_escape: self.disable_escape,
137 }
138 }
139
get_partial(&self, name: &str) -> Option<Template>140 pub fn get_partial(&self, name: &str) -> Option<Template> {
141 self.partials.get(name).map(|t| t.clone())
142 }
143
set_partial(&mut self, name: String, result: Template)144 pub fn set_partial(&mut self, name: String, result: Template) {
145 self.partials.insert(name, result);
146 }
147
get_path(&self) -> &String148 pub fn get_path(&self) -> &String {
149 &self.path
150 }
151
set_path(&mut self, path: String)152 pub fn set_path(&mut self, path: String) {
153 self.path = path;
154 }
155
get_local_path_root(&self) -> &VecDeque<String>156 pub fn get_local_path_root(&self) -> &VecDeque<String> {
157 &self.local_path_root
158 }
159
push_local_path_root(&mut self, path: String)160 pub fn push_local_path_root(&mut self, path: String) {
161 self.local_path_root.push_front(path)
162 }
163
pop_local_path_root(&mut self)164 pub fn pop_local_path_root(&mut self) {
165 self.local_path_root.pop_front();
166 }
167
set_local_var(&mut self, name: String, value: Json)168 pub fn set_local_var(&mut self, name: String, value: Json) {
169 self.local_variables.insert(name, value);
170 }
171
clear_local_vars(&mut self)172 pub fn clear_local_vars(&mut self) {
173 self.local_variables.clear();
174 }
175
promote_local_vars(&mut self)176 pub fn promote_local_vars(&mut self) {
177 let mut new_map: HashMap<String, Json> = HashMap::new();
178 for key in self.local_variables.keys() {
179 let mut new_key = String::new();
180 new_key.push_str("@../");
181 new_key.push_str(&key[1..]);
182
183 let v = self.local_variables.get(key).unwrap().clone();
184 new_map.insert(new_key, v);
185 }
186 self.local_variables = new_map;
187 }
188
demote_local_vars(&mut self)189 pub fn demote_local_vars(&mut self) {
190 let mut new_map: HashMap<String, Json> = HashMap::new();
191 for key in self.local_variables.keys() {
192 if key.starts_with("@../") {
193 let mut new_key = String::new();
194 new_key.push('@');
195 new_key.push_str(&key[4..]);
196
197 let v = self.local_variables.get(key).unwrap().clone();
198 new_map.insert(new_key, v);
199 }
200 }
201 self.local_variables = new_map;
202 }
203
get_local_var(&self, name: &String) -> Option<&Json>204 pub fn get_local_var(&self, name: &String) -> Option<&Json> {
205 self.local_variables.get(name)
206 }
207
writer(&mut self) -> &mut Write208 pub fn writer(&mut self) -> &mut Write {
209 self.writer
210 }
211
push_block_context<T>(&mut self, ctx: &T) where T: ToJson212 pub fn push_block_context<T>(&mut self, ctx: &T)
213 where T: ToJson
214 {
215 self.block_context.push_front(Context::wraps(ctx));
216 }
217
pop_block_context(&mut self)218 pub fn pop_block_context(&mut self) {
219 self.block_context.pop_front();
220 }
221
evaluate_in_block_context(&self, local_path: &str) -> Option<&Json>222 pub fn evaluate_in_block_context(&self, local_path: &str) -> Option<&Json> {
223 for bc in self.block_context.iter() {
224 let v = bc.navigate(".", &self.local_path_root, local_path);
225 if !v.is_null() {
226 return Some(v);
227 }
228 }
229
230 None
231 }
232
is_current_template(&self, p: &str) -> bool233 pub fn is_current_template(&self, p: &str) -> bool {
234 self.current_template.as_ref().map(|s| s == p).unwrap_or(false)
235 }
236 }
237
238 impl<'a> fmt::Debug for RenderContext<'a> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>239 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
240 write!(f,
241 "partials: {:?}, path: {:?}, local_variables: {:?}, current_template: {:?}, \
242 root_template: {:?}, disable_escape: {:?}, local_path_root: {:?}",
243 self.partials,
244 self.path,
245 self.local_variables,
246 self.current_template,
247 self.root_template,
248 self.disable_escape,
249 self.local_path_root)
250 }
251 }
252
253 /// Json wrapper that holds the Json value and reference path information
254 ///
255 #[derive(Debug)]
256 pub struct ContextJson {
257 path: Option<String>,
258 value: Json,
259 }
260
261 impl ContextJson {
262 /// Returns relative path when the value is referenced
263 /// If the value is from a literal, the path is `None`
path(&self) -> Option<&String>264 pub fn path(&self) -> Option<&String> {
265 self.path.as_ref()
266 }
267
268 /// Return root level of this path if any
path_root(&self) -> Option<&str>269 pub fn path_root(&self) -> Option<&str> {
270 self.path.as_ref().and_then(|p| p.split(|c| c == '.' || c == '/').nth(0))
271 }
272
273 /// Returns the value
value(&self) -> &Json274 pub fn value(&self) -> &Json {
275 &self.value
276 }
277 }
278
279 /// Render-time Helper data when using in a helper definition
280 pub struct Helper<'a> {
281 name: &'a str,
282 params: Vec<ContextJson>,
283 hash: BTreeMap<String, ContextJson>,
284 block_param: &'a Option<BlockParam>,
285 template: &'a Option<Template>,
286 inverse: &'a Option<Template>,
287 block: bool,
288 }
289
290 impl<'a, 'b> Helper<'a> {
from_template(ht: &'a HelperTemplate, ctx: &Context, registry: &Registry, rc: &'b mut RenderContext) -> Result<Helper<'a>, RenderError>291 fn from_template(ht: &'a HelperTemplate,
292 ctx: &Context,
293 registry: &Registry,
294 rc: &'b mut RenderContext)
295 -> Result<Helper<'a>, RenderError> {
296 let mut evaluated_params = Vec::new();
297 for p in ht.params.iter() {
298 let r = try!(p.expand(ctx, registry, rc));
299 evaluated_params.push(r);
300 }
301
302 let mut evaluated_hash = BTreeMap::new();
303 for (k, p) in ht.hash.iter() {
304 let r = try!(p.expand(ctx, registry, rc));
305 evaluated_hash.insert(k.clone(), r);
306 }
307
308 Ok(Helper {
309 name: &ht.name,
310 params: evaluated_params,
311 hash: evaluated_hash,
312 block_param: &ht.block_param,
313 template: &ht.template,
314 inverse: &ht.inverse,
315 block: ht.block,
316 })
317 }
318
319 /// Returns helper name
name(&self) -> &str320 pub fn name(&self) -> &str {
321 &self.name
322 }
323
324 /// Returns all helper params, resolved within the context
params(&self) -> &Vec<ContextJson>325 pub fn params(&self) -> &Vec<ContextJson> {
326 &self.params
327 }
328
329 /// Returns nth helper param, resolved within the context
param(&self, idx: usize) -> Option<&ContextJson>330 pub fn param(&self, idx: usize) -> Option<&ContextJson> {
331 self.params.get(idx)
332 }
333
334 /// Returns hash, resolved within the context
hash(&self) -> &BTreeMap<String, ContextJson>335 pub fn hash(&self) -> &BTreeMap<String, ContextJson> {
336 &self.hash
337 }
338
339 /// Return hash value of a given key, resolved within the context
hash_get(&self, key: &str) -> Option<&ContextJson>340 pub fn hash_get(&self, key: &str) -> Option<&ContextJson> {
341 self.hash.get(key)
342 }
343
344 /// Returns the default inner template if any
template(&self) -> Option<&Template>345 pub fn template(&self) -> Option<&Template> {
346 (*self.template).as_ref().map(|t| t)
347 }
348
349 /// Returns the template of `else` branch if any
inverse(&self) -> Option<&Template>350 pub fn inverse(&self) -> Option<&Template> {
351 (*self.inverse).as_ref().map(|t| t)
352 }
353
354 /// Returns if the helper is a block one `{{#helper}}{{/helper}}` or not `{{helper 123}}`
is_block(&self) -> bool355 pub fn is_block(&self) -> bool {
356 self.block
357 }
358
359 /// Returns block param if any
block_param(&self) -> Option<&str>360 pub fn block_param(&self) -> Option<&str> {
361 if let Some(BlockParam::Single(Parameter::Name(ref s))) = *self.block_param {
362 Some(s)
363 } else {
364 None
365 }
366 }
367
368 /// Return block param pair (for example |key, val|) if any
block_param_pair(&self) -> Option<(&str, &str)>369 pub fn block_param_pair(&self) -> Option<(&str, &str)> {
370 if let Some(BlockParam::Pair((Parameter::Name(ref s1), Parameter::Name(ref s2)))) =
371 *self.block_param {
372 Some((s1, s2))
373 } else {
374 None
375 }
376 }
377 }
378
379 pub struct Directive<'a> {
380 name: String,
381 params: Vec<ContextJson>,
382 hash: BTreeMap<String, ContextJson>,
383 template: &'a Option<Template>,
384 }
385
386 impl<'a, 'b> Directive<'a> {
from_template(dt: &'a DirectiveTemplate, ctx: &Context, registry: &Registry, rc: &'b mut RenderContext) -> Result<Directive<'a>, RenderError>387 pub fn from_template(dt: &'a DirectiveTemplate,
388 ctx: &Context,
389 registry: &Registry,
390 rc: &'b mut RenderContext)
391 -> Result<Directive<'a>, RenderError> {
392 let name = try!(dt.name.expand_as_name(ctx, registry, rc));
393
394 let mut evaluated_params = Vec::new();
395 for p in dt.params.iter() {
396 let r = try!(p.expand(ctx, registry, rc));
397 evaluated_params.push(r);
398 }
399
400 let mut evaluated_hash = BTreeMap::new();
401 for (k, p) in dt.hash.iter() {
402 let r = try!(p.expand(ctx, registry, rc));
403 evaluated_hash.insert(k.clone(), r);
404 }
405
406 Ok(Directive {
407 name: name,
408 params: evaluated_params,
409 hash: evaluated_hash,
410 template: &dt.template,
411 })
412 }
413
414 /// Returns helper name
name(&self) -> &str415 pub fn name(&self) -> &str {
416 &self.name
417 }
418
419 /// Returns all helper params, resolved within the context
params(&self) -> &Vec<ContextJson>420 pub fn params(&self) -> &Vec<ContextJson> {
421 &self.params
422 }
423
424 /// Returns nth helper param, resolved within the context
param(&self, idx: usize) -> Option<&ContextJson>425 pub fn param(&self, idx: usize) -> Option<&ContextJson> {
426 self.params.get(idx)
427 }
428
429 /// Returns hash, resolved within the context
hash(&self) -> &BTreeMap<String, ContextJson>430 pub fn hash(&self) -> &BTreeMap<String, ContextJson> {
431 &self.hash
432 }
433
434 /// Return hash value of a given key, resolved within the context
hash_get(&self, key: &str) -> Option<&ContextJson>435 pub fn hash_get(&self, key: &str) -> Option<&ContextJson> {
436 self.hash.get(key)
437 }
438
439 /// Returns the default inner template if any
template(&self) -> Option<&Template>440 pub fn template(&self) -> Option<&Template> {
441 (*self.template).as_ref().map(|t| t)
442 }
443 }
444
445 pub trait Renderable {
render(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>446 fn render(&self,
447 ctx: &Context,
448 registry: &Registry,
449 rc: &mut RenderContext)
450 -> Result<(), RenderError>;
451 }
452
453 pub trait Evalable {
eval(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>454 fn eval(&self,
455 ctx: &Context,
456 registry: &Registry,
457 rc: &mut RenderContext)
458 -> Result<(), RenderError>;
459 }
460
461
462 impl Parameter {
expand_as_name(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<String, RenderError>463 pub fn expand_as_name(&self,
464 ctx: &Context,
465 registry: &Registry,
466 rc: &mut RenderContext)
467 -> Result<String, RenderError> {
468 match self {
469 &Parameter::Name(ref name) => Ok(name.to_owned()),
470 &Parameter::Subexpression(ref t) => {
471 let mut local_writer = StringWriter::new();
472 {
473 let mut local_rc = rc.with_writer(&mut local_writer);
474 // disable html escape for subexpression
475 local_rc.disable_escape = true;
476
477 try!(t.as_template().render(ctx, registry, &mut local_rc));
478 }
479
480 Ok(local_writer.to_string())
481 }
482 &Parameter::Literal(ref j) => Ok(j.render()),
483 }
484 }
485
expand(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<ContextJson, RenderError>486 pub fn expand(&self,
487 ctx: &Context,
488 registry: &Registry,
489 rc: &mut RenderContext)
490 -> Result<ContextJson, RenderError> {
491 match self {
492 &Parameter::Name(ref name) => {
493 Ok(rc.get_local_var(&name).map_or_else(|| {
494 ContextJson {
495 path: Some(name.to_owned()),
496 value: rc.evaluate_in_block_context(name).map_or_else(|| {ctx.navigate(rc.get_path(), rc.get_local_path_root(), name).clone()}, |v| v.clone()),
497 }
498
499 },
500 |v| {
501 ContextJson {
502 path: None,
503 value: v.clone(),
504 }
505 }))
506 }
507 &Parameter::Literal(ref j) => {
508 Ok(ContextJson {
509 path: None,
510 value: j.clone(),
511 })
512 }
513 &Parameter::Subexpression(ref t) => {
514 let mut local_writer = StringWriter::new();
515 let result = {
516 let mut local_rc = rc.with_writer(&mut local_writer);
517 // disable html escape for subexpression
518 local_rc.disable_escape = true;
519
520 t.as_template().render(ctx, registry, &mut local_rc)
521 };
522
523 match result {
524 Ok(_) => {
525 let n = local_writer.to_string();
526 try!(Parameter::parse(&n).map_err(|_| {
527 RenderError::new("subexpression generates invalid value")
528 }))
529 .expand(ctx, registry, rc)
530 }
531 Err(e) => Err(e),
532 }
533 }
534 }
535 }
536 }
537
538 impl Renderable for Template {
render(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>539 fn render(&self,
540 ctx: &Context,
541 registry: &Registry,
542 rc: &mut RenderContext)
543 -> Result<(), RenderError> {
544 rc.current_template = self.name.clone();
545 let iter = self.elements.iter();
546 let mut idx = 0;
547 for t in iter {
548 let c = ctx;
549 try!(t.render(c, registry, rc).map_err(|mut e| {
550 if e.line_no.is_none() {
551 if let Some(ref mapping) = self.mapping {
552 if let Some(&TemplateMapping(line, col)) = mapping.get(idx) {
553 e.line_no = Some(line);
554 e.column_no = Some(col);
555
556 }
557 }
558 }
559
560 e.template_name = self.name.clone();
561 e
562 }));
563 idx = idx + 1;
564 }
565 Ok(())
566 }
567 }
568
569 impl Evalable for Template {
eval(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>570 fn eval(&self,
571 ctx: &Context,
572 registry: &Registry,
573 rc: &mut RenderContext)
574 -> Result<(), RenderError> {
575 let iter = self.elements.iter();
576 let mut idx = 0;
577 for t in iter {
578 let c = ctx;
579 try!(t.eval(c, registry, rc).map_err(|mut e| {
580 if e.line_no.is_none() {
581 if let Some(ref mapping) = self.mapping {
582 if let Some(&TemplateMapping(line, col)) = mapping.get(idx) {
583 e.line_no = Some(line);
584 e.column_no = Some(col);
585
586 }
587 }
588 }
589
590 e.template_name = self.name.clone();
591 e
592 }));
593 idx = idx + 1;
594 }
595 Ok(())
596 }
597 }
598
599 impl Renderable for TemplateElement {
render(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>600 fn render(&self,
601 ctx: &Context,
602 registry: &Registry,
603 rc: &mut RenderContext)
604 -> Result<(), RenderError> {
605 debug!("rendering {:?}, {:?}", self, rc);
606 match *self {
607 RawString(ref v) => {
608 try!(rc.writer.write(v.clone().into_bytes().as_ref()));
609 Ok(())
610 }
611 Expression(ref v) => {
612 let context_json = try!(v.expand(ctx, registry, rc));
613 let rendered = context_json.value.render();
614
615 let output = if !rc.disable_escape {
616 registry.get_escape_fn()(&rendered)
617 } else {
618 rendered
619 };
620 try!(rc.writer.write(output.into_bytes().as_ref()));
621 Ok(())
622 }
623 HTMLExpression(ref v) => {
624 let context_json = try!(v.expand(ctx, registry, rc));
625 let rendered = context_json.value.render();
626 try!(rc.writer.write(rendered.into_bytes().as_ref()));
627 Ok(())
628 }
629 HelperExpression(ref ht) | HelperBlock(ref ht) => {
630 let helper = try!(Helper::from_template(ht, ctx, registry, rc));
631 match registry.get_helper(&ht.name) {
632 Some(d) => (**d).call(ctx, &helper, registry, rc),
633 None => {
634 let meta_helper_name = if ht.block {
635 "blockHelperMissing"
636 } else {
637 "helperMissing"
638 }
639 .to_string();
640 match registry.get_helper(&meta_helper_name) {
641 Some(md) => (**md).call(ctx, &helper, registry, rc),
642 None => {
643 Err(RenderError::new(format!("Helper not defined: {:?}", ht.name)))
644 }
645 }
646 }
647 }
648 }
649 DirectiveExpression(_) | DirectiveBlock(_) | PartialExpression(_) | PartialBlock(_) => {
650 self.eval(ctx, registry, rc)
651 }
652 _ => Ok(()),
653 }
654 }
655 }
656
657 impl Evalable for TemplateElement {
eval(&self, ctx: &Context, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>658 fn eval(&self,
659 ctx: &Context,
660 registry: &Registry,
661 rc: &mut RenderContext)
662 -> Result<(), RenderError> {
663 match *self {
664 DirectiveExpression(ref dt) | DirectiveBlock(ref dt) => {
665 Directive::from_template(dt, ctx, registry, rc).and_then(|di| {
666 match registry.get_decorator(&di.name) {
667 Some(d) => (**d).call(ctx, &di, registry, rc),
668 None => {
669 Err(RenderError::new(format!("Directive not defined: {:?}", dt.name)))
670 }
671 }
672 })
673 }
674 #[cfg(feature="partial4")]
675 PartialExpression(ref dt) | PartialBlock(ref dt) => {
676 Directive::from_template(dt, ctx, registry, rc)
677 .and_then(|di| partial::expand_partial(ctx, &di, registry, rc))
678 }
679 _ => Ok(()),
680 }
681 }
682 }
683
684 #[test]
test_raw_string()685 fn test_raw_string() {
686 let r = Registry::new();
687 let mut sw = StringWriter::new();
688 {
689 let mut rc = RenderContext::new(&mut sw);
690 let raw_string = RawString("<h1>hello world</h1>".to_string());
691
692 raw_string.render(&Context::null(), &r, &mut rc).ok().unwrap();
693 }
694 assert_eq!(sw.to_string(), "<h1>hello world</h1>".to_string());
695 }
696
697 #[test]
test_expression()698 fn test_expression() {
699 let r = Registry::new();
700 let mut sw = StringWriter::new();
701 {
702 let mut rc = RenderContext::new(&mut sw);
703 let element = Expression(Parameter::Name("hello".into()));
704 let mut m: HashMap<String, String> = HashMap::new();
705 let value = "<p></p>".to_string();
706
707 m.insert("hello".to_string(), value);
708
709 let ctx = Context::wraps(&m);
710
711 element.render(&ctx, &r, &mut rc).ok().unwrap();
712 }
713
714 assert_eq!(sw.to_string(), "<p></p>".to_string());
715 }
716
717 #[test]
test_html_expression()718 fn test_html_expression() {
719 let r = Registry::new();
720 let mut sw = StringWriter::new();
721 let value = "world";
722 {
723 let mut rc = RenderContext::new(&mut sw);
724 let element = HTMLExpression(Parameter::Name("hello".into()));
725 let mut m: HashMap<String, String> = HashMap::new();
726
727 m.insert("hello".to_string(), value.to_string());
728
729 let ctx = Context::wraps(&m);
730 element.render(&ctx, &r, &mut rc).ok().unwrap();
731 }
732
733 assert_eq!(sw.to_string(), value.to_string());
734 }
735
736 #[test]
test_template()737 fn test_template() {
738 let r = Registry::new();
739 let mut sw = StringWriter::new();
740 {
741 let mut rc = RenderContext::new(&mut sw);
742 let mut elements: Vec<TemplateElement> = Vec::new();
743
744 let e1 = RawString("<h1>".to_string());
745 elements.push(e1);
746
747 let e2 = Expression(Parameter::Name("hello".into()));
748 elements.push(e2);
749
750 let e3 = RawString("</h1>".to_string());
751 elements.push(e3);
752
753 let e4 = Comment("".to_string());
754 elements.push(e4);
755
756 let template = Template {
757 elements: elements,
758 name: None,
759 mapping: None,
760 };
761
762 let mut m: HashMap<String, String> = HashMap::new();
763 let value = "world".to_string();
764 m.insert("hello".to_string(), value);
765
766 let ctx = Context::wraps(&m);
767 template.render(&ctx, &r, &mut rc).ok().unwrap();
768 }
769
770 assert_eq!(sw.to_string(), "<h1>world</h1>".to_string());
771 }
772
773 #[test]
774 #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
test_render_context_promotion_and_demotion()775 fn test_render_context_promotion_and_demotion() {
776 use serialize::json::ToJson;
777 let mut sw = StringWriter::new();
778 let mut render_context = RenderContext::new(&mut sw);
779
780 render_context.set_local_var("@index".to_string(), 0usize.to_json());
781
782 render_context.promote_local_vars();
783
784 assert_eq!(render_context.get_local_var(&"@../index".to_string()).unwrap(),
785 &0usize.to_json());
786
787 render_context.demote_local_vars();
788
789 assert_eq!(render_context.get_local_var(&"@index".to_string()).unwrap(),
790 &0usize.to_json());
791 }
792
793 #[test]
test_render_subexpression()794 fn test_render_subexpression() {
795 let r = Registry::new();
796 let mut sw = StringWriter::new();
797 {
798 let mut rc = RenderContext::new(&mut sw);
799 let template = Template::compile("<h1>{{#if (const)}}{{(hello)}}{{/if}}</h1>").unwrap();
800
801 let mut m: HashMap<String, String> = HashMap::new();
802 m.insert("hello".to_string(), "world".to_string());
803 m.insert("world".to_string(), "nice".to_string());
804 m.insert("const".to_string(), "\"truthy\"".to_string());
805
806 let ctx = Context::wraps(&m);
807 if let Err(e) = template.render(&ctx, &r, &mut rc) {
808 panic!("{}", e);
809 }
810 }
811
812 assert_eq!(sw.to_string(), "<h1>nice</h1>".to_string());
813 }
814
815 #[test]
test_render_error_line_no()816 fn test_render_error_line_no() {
817 let r = Registry::new();
818 let mut sw = StringWriter::new();
819 let mut rc = RenderContext::new(&mut sw);
820 let name = "invalid_template";
821 let mut template = Template::compile2("<h1>\n{{#if true}}\n {{#each}}{{/each}}\n{{/if}}",
822 true)
823 .unwrap();
824 template.name = Some(name.to_owned());
825
826 let m: HashMap<String, String> = HashMap::new();
827
828 let ctx = Context::wraps(&m);
829 if let Err(e) = template.render(&ctx, &r, &mut rc) {
830 assert_eq!(e.line_no.unwrap(), 3);
831 assert_eq!(e.column_no.unwrap(), 3);
832 assert_eq!(e.template_name, Some(name.to_owned()));
833 } else {
834 panic!("Error expected");
835 }
836 }
837