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