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(), "&lt;p&gt;&lt;/p&gt;".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