bracket/render/
mod.rs

1//! Render a template to output using the data.
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::fmt;
5use std::rc::Rc;
6
7use serde::Serialize;
8use serde_json::{Map, Value};
9
10use crate::{
11    error::{HelperError, RenderError},
12    helper::{Helper, HelperResult, LocalHelper},
13    json,
14    output::{Output, StringOutput},
15    parser::{
16        ast::{
17            Block, Call, CallTarget, Lines, Link, Node, ParameterValue, Path,
18            Slice,
19        },
20        path,
21    },
22    template::Template,
23    trim::{TrimHint, TrimState},
24    Registry, RenderResult,
25};
26
27const PARTIAL_BLOCK: &str = "@partial-block";
28const HELPER_MISSING: &str = "helperMissing";
29const BLOCK_HELPER_MISSING: &str = "blockHelperMissing";
30const HELPER_LINK: &str = "link";
31
32type HelperValue = Option<Value>;
33
34pub mod assert;
35pub mod context;
36pub mod scope;
37
38pub use assert::{assert, Type};
39pub use context::{Context, MissingValue, Property};
40pub use scope::Scope;
41
42/// Maximum stack size for helper calls
43const STACK_MAX: usize = 32;
44
45enum HelperTarget<'a> {
46    Name(&'a str),
47    Helper(&'a Box<dyn Helper + 'a>),
48}
49
50/// Call site keeps track of calls so we can
51/// detect cyclic calls and therefore prevent a
52/// stack overflow by returning a render
53/// error when a cycle is detected.
54///
55/// Note that we must distinguish between helper
56/// types otherwise the `if` helper will not work
57/// as expected as it returns values and handles
58/// block templates.
59#[derive(Eq, PartialEq, Hash, Debug, Clone)]
60pub enum CallSite {
61    /// Call site for a partial render.
62    Partial(String),
63    /// Call site for a helper.
64    Helper(String),
65    /// Call site for a block helper.
66    BlockHelper(String),
67}
68
69impl fmt::Display for CallSite {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(f, "{}", {
72            match *self {
73                CallSite::Partial(ref name) => format!("partial#{}", name),
74                CallSite::Helper(ref name) => format!("helper#{}", name),
75                CallSite::BlockHelper(ref name) => format!("block#{}", name),
76            }
77        })
78    }
79}
80
81impl Into<String> for CallSite {
82    fn into(self) -> String {
83        match self {
84            CallSite::Partial(name)
85            | CallSite::Helper(name)
86            | CallSite::BlockHelper(name) => name,
87        }
88    }
89}
90
91/// Render a template.
92pub struct Render<'render> {
93    registry: &'render Registry<'render>,
94    local_helpers: Rc<RefCell<HashMap<String, Box<dyn LocalHelper + 'render>>>>,
95    partials: HashMap<String, &'render Node<'render>>,
96    name: &'render str,
97    root: Value,
98    writer: Box<&'render mut dyn Output>,
99    scopes: Vec<Scope>,
100    trim: TrimState,
101    hint: Option<TrimHint>,
102    end_tag_hint: Option<TrimHint>,
103    stack: Vec<CallSite>,
104    current_partial_name: Vec<Option<&'render str>>,
105}
106
107impl<'render> Render<'render> {
108    /// Create a renderer.
109    ///
110    /// You should not need to create a renderer directly, instead
111    /// use the functions provided by the `Registry`.
112    pub fn new<T>(
113        registry: &'render Registry<'render>,
114        name: &'render str,
115        data: &T,
116        writer: Box<&'render mut dyn Output>,
117        stack: Vec<CallSite>,
118    ) -> RenderResult<Self>
119    where
120        T: Serialize,
121    {
122        let root = serde_json::to_value(data).map_err(RenderError::from)?;
123        let scopes: Vec<Scope> = Vec::new();
124
125        Ok(Self {
126            registry,
127            local_helpers: Rc::new(RefCell::new(HashMap::new())),
128            partials: HashMap::new(),
129            name,
130            root,
131            writer,
132            scopes,
133            trim: Default::default(),
134            hint: None,
135            end_tag_hint: None,
136            stack,
137            current_partial_name: Vec::new(),
138        })
139    }
140
141    /// Get the name of the template being rendered.
142    ///
143    /// This will equal the name given when the renderer is started 
144    /// and does not account for partials; to get the name of a template 
145    /// including the current partial use [current_name()](#method.current_name).
146    pub fn template_name(&self) -> &str {
147        self.name 
148    }
149
150    /// Get the current name for the template being rendered.
151    ///
152    /// When a partial is being rendered this will return the name 
153    /// for the current partial otherwise it falls back to the name 
154    /// given when the renderer was started.
155    ///
156    /// When partials have been loaded from the file system 
157    /// the name will be the file path and should be safe to 
158    /// pass to `PathBuf::from` to get a reference to the original file.
159    ///
160    /// It is the caller's responsiblity to account for relative paths 
161    /// when converting template names to paths.
162    pub fn current_name(&self) -> &str {
163        if !self.current_partial_name.is_empty() {
164            self.current_partial_name.last().unwrap().unwrap_or(self.name)
165        } else { self.name }
166    }
167
168    /// Render template string content and return the buffered result.
169    ///
170    /// The current call site stack is cloned and used in a new render 
171    /// pass so that cyclic calls can be detected before the stack overflows.
172    ///
173    /// Use this function when a helper wants to render a 
174    /// dynamic template but needs to prevent a stack overflow when a 
175    /// cyclic call is detected.
176    pub fn once<T>(
177        &self,
178        file_name: &str,
179        content: &str,
180        data: &T,
181    ) -> HelperResult<String>
182    where
183        T: Serialize,
184    {
185        let result = self
186            .registry()
187            .once_stack(file_name, &content, data, self.stack.clone())
188            .map_err(|e| HelperError::new(e.to_string()))?;
189        Ok(result)
190    }
191
192    /// Get a reference to the registry.
193    pub fn registry(&self) -> &Registry<'_> {
194        self.registry
195    }
196
197    /// Render a node by iterating it's children.
198    ///
199    /// The supplied node should be a document or block node.
200    pub fn render(&mut self, node: &'render Node<'render>) -> RenderResult<()> {
201        for event in node.into_iter().event(Default::default()) {
202            self.render_node(event.node, event.trim)?;
203        }
204        Ok(())
205    }
206
207    /// Get a named template.
208    pub fn get_template(&self, name: &str) -> Option<&'render Template> {
209        self.registry.get(name)
210    }
211
212    /// Get a mutable reference to the output destination.
213    ///
214    /// You should prefer the `write()` and `write_escaped()` functions
215    /// when writing strings but if you want to write bytes directly to
216    /// the output destination you can use this reference.
217    pub fn out(&mut self) -> &mut Box<&'render mut dyn Output> {
218        &mut self.writer
219    }
220
221    /// Escape a value using the current escape function.
222    pub fn escape(&self, val: &str) -> String {
223        (self.registry.escape())(val)
224    }
225
226    /// Write a string to the output destination.
227    pub fn write(&mut self, s: &str) -> HelperResult<usize> {
228        self.write_str(s, false)
229            .map_err(Box::new)
230            .map_err(HelperError::from)
231    }
232
233    /// Write a string to the output destination and escape the content
234    /// using the current escape function.
235    pub fn write_escaped(&mut self, s: &str) -> HelperResult<usize> {
236        self.write_str(s, true)
237            .map_err(Box::new)
238            .map_err(HelperError::from)
239    }
240
241    /// Push a scope onto the stack.
242    pub fn push_scope(&mut self, scope: Scope) {
243        self.scopes.push(scope);
244    }
245
246    /// Remove a scope from the stack.
247    pub fn pop_scope(&mut self) -> Option<Scope> {
248        self.scopes.pop()
249    }
250
251    /// Get a mutable reference to the current scope.
252    pub fn scope_mut(&mut self) -> Option<&mut Scope> {
253        self.scopes.last_mut()
254    }
255
256    /// Reference to the root data for the render.
257    pub fn data(&self) -> &Value {
258        &self.root
259    }
260
261    /// Evaluate the block conditionals and find
262    /// the first node that should be rendered.
263    pub fn inverse<'a>(
264        &mut self,
265        template: &'a Node<'a>,
266    ) -> Result<Option<&'a Node<'a>>, HelperError> {
267        let mut alt: Option<&'a Node<'_>> = None;
268        let mut branch: Option<&'a Node<'_>> = None;
269
270        match template {
271            Node::Block(ref block) => {
272                if !block.conditions().is_empty() {
273                    for node in block.conditions().iter() {
274                        match node {
275                            Node::Block(clause) => {
276                                // Got an else clause, last one wins!
277                                if clause.call().is_empty() {
278                                    alt = Some(node);
279                                } else {
280                                    if let Some(value) = self
281                                        .call(clause.call())
282                                        .map_err(Box::new)?
283                                    {
284                                        if json::is_truthy(&value) {
285                                            branch = Some(node);
286                                            break;
287                                        }
288                                    }
289                                }
290                            }
291                            _ => {}
292                        }
293                    }
294                }
295            }
296            _ => {}
297        }
298
299        Ok(branch.or(alt))
300    }
301
302    /// Render an inner template.
303    ///
304    /// Block helpers should call this when they want to render an inner template.
305    pub fn template(
306        &mut self,
307        node: &'render Node<'render>,
308    ) -> Result<(), HelperError> {
309        let mut hint: Option<TrimHint> = None;
310        for event in node.into_iter().event(self.hint) {
311            let mut trim = event.trim;
312
313            if event.first {
314                let hint = node.trim();
315                if hint.after {
316                    trim.start = true;
317                }
318            }
319
320            if event.last {
321                match node {
322                    Node::Block(ref block) => {
323                        let last_hint = block.trim_close();
324                        if last_hint.before {
325                            trim.end = true;
326                        }
327                        hint = Some(last_hint);
328                    }
329                    _ => {}
330                }
331            }
332
333            self.render_node(event.node, trim)
334                .map_err(|e| HelperError::Render(Box::new(e)))?;
335        }
336
337        // Store the hint so we can remove leading whitespace
338        // after a block end tag
339        self.end_tag_hint = hint;
340
341        Ok(())
342    }
343
344    /// Render a node and buffer the result to a string.
345    ///
346    /// The call stack and scopes are inherited from this renderer.
347    ///
348    /// The supplied node should be a document or block node.
349    pub fn buffer(
350        &self,
351        node: &'render Node<'render>,
352    ) -> Result<String, HelperError> {
353        let mut writer = StringOutput::new();
354        let mut rc = Render::new(
355            self.registry,
356            self.name,
357            &self.root,
358            Box::new(&mut writer),
359            self.stack.clone(),
360        )
361        .map_err(Box::new)?;
362
363        // Inherit the stack and scope from this renderer
364        rc.stack = self.stack.clone();
365        rc.scopes = self.scopes.clone();
366
367        // NOTE: call `template()` not `render()` so trim settings
368        // NOTE: on the parent node are respected!
369        rc.template(node)?;
370
371        // Must drop the renderer to take ownership of the string buffer
372        drop(rc);
373
374        Ok(writer.into())
375    }
376
377    /// Evaluate a path and return the resolved value.
378    ///
379    /// This allows helpers to find variables in the template data
380    /// using the familiar path syntax such as `@root.name`.
381    ///
382    /// Paths are evaluated using the current scope so local variables
383    /// in the current scope will be resolved.
384    ///
385    /// Paths are dynamically evaluated so syntax errors are caught and
386    /// returned wrapped as `HelperError`.
387    ///
388    /// Sub-expressions are not executed.
389    pub fn evaluate<'a>(
390        &'a self,
391        value: &str,
392    ) -> HelperResult<Option<&'a Value>> {
393        if let Some(path) = path::from_str(value)? {
394            return Ok(self.lookup(&path));
395        }
396        Ok(None)
397    }
398
399    /// Evaluate a path and perform a type assertion on the value.
400    ///
401    /// If no value exists for the given path the value is
402    /// treated as null and type assertion is performed on the
403    /// null value.
404    pub fn try_evaluate<'a>(
405        &'a self,
406        value: &str,
407        kinds: &[Type],
408    ) -> HelperResult<&'a Value> {
409        let val = self.evaluate(value)?.or(Some(&Value::Null)).unwrap();
410        let (result, kind) = assert(val, kinds);
411        if !result {
412            return Err(HelperError::TypeAssert(
413                value.to_string(),
414                kind.unwrap(),
415                Type::from(val).to_string(),
416            ));
417        }
418        Ok(val)
419    }
420
421    /// Infallible variable lookup by path.
422    fn lookup<'a>(&'a self, path: &Path<'_>) -> Option<&'a Value> {
423        //println!("Lookup path {:?}", path.as_str());
424        //println!("Lookup path {:?}", path);
425
426
427        // Absolute paths should never be resolved to variables
428        // the correct syntax is to use `@root` to resolve a 
429        // variable from the root of the template data.
430        if path.absolute() {
431            return None; 
432        }
433
434        // Handle explicit `@root` reference
435        if path.is_root() {
436            json::find_parts(
437                path.components().iter().skip(1).map(|c| c.as_value()),
438                &self.root,
439            )
440        // Handle explicit this
441        } else if path.is_explicit() {
442            let value = if let Some(scope) = self.scopes.last() {
443                if let Some(base) = scope.base_value() {
444                    base
445                } else {
446                    &self.root
447                }
448            } else {
449                &self.root
450            };
451
452            // Handle explicit this only
453            if path.components().len() == 1 {
454                Some(value)
455            // Otherwise lookup in this context
456            } else {
457                json::find_parts(
458                    path.components().iter().skip(1).map(|c| c.as_value()),
459                    value,
460                )
461            }
462        // Handle local @variable references which must
463        // be resolved using the current scope
464        } else if path.is_local() {
465            if let Some(scope) = self.scopes.last() {
466                json::find_parts(
467                    path.components().iter().map(|c| c.as_value()),
468                    scope.locals(),
469                )
470            } else {
471                None
472            }
473        } else if path.parents() > 0 {
474            let mut all: Vec<(&Value, Option<&Value>)> = self
475                .scopes
476                .iter()
477                .map(|s| (s.locals(), s.base_value().as_ref()))
478                .collect();
479
480            // Combine so that the root object is
481            // treated as a scope
482            all.insert(0, (&self.root, None));
483
484            if all.len() > path.parents() as usize {
485                let index: usize = all.len() - (path.parents() as usize + 1);
486                if let Some((locals, value)) = all.get(index) {
487                    if let Some(res) = json::find_parts(
488                        path.components().iter().map(|c| c.as_value()),
489                        locals,
490                    ) {
491                        return Some(res);
492                    } else if let Some(value) = value {
493                        if let Some(res) = json::find_parts(
494                            path.components().iter().map(|c| c.as_value()),
495                            value,
496                        ) {
497                            return Some(res);
498                        }
499                    }
500                    None
501                } else {
502                    None
503                }
504            } else {
505                None
506            }
507        } else {
508            let mut values: Vec<(&Value, Option<&Value>)> = self
509                .scopes
510                .iter()
511                .map(|v| (v.locals(), v.base_value().as_ref()))
512                .rev()
513                .collect();
514            values.push((&self.root, None));
515
516            for (locals, value) in values {
517                if let Some(res) = json::find_parts(
518                    path.components().iter().map(|c| c.as_value()),
519                    locals,
520                ) {
521                    return Some(res);
522                } else if let Some(value) = value {
523                    if let Some(res) = json::find_parts(
524                        path.components().iter().map(|c| c.as_value()),
525                        value,
526                    ) {
527                        return Some(res);
528                    }
529                }
530            }
531            None
532        }
533    }
534
535    /// Create the context arguments list.
536    fn arguments(
537        &mut self,
538        call: &Call<'_>,
539        missing: &mut Vec<MissingValue>,
540    ) -> RenderResult<Vec<Value>> {
541        let mut out: Vec<Value> = Vec::new();
542        for (i, p) in call.arguments().iter().enumerate() {
543            let arg = match p {
544                ParameterValue::Json { ref value, .. } => value.clone(),
545                ParameterValue::Path(ref path) => {
546                    self.lookup(path).cloned().unwrap_or_else(|| {
547                        missing.push(MissingValue::Argument(
548                            i,
549                            Value::String(path.as_str().to_string()),
550                        ));
551                        Value::Null
552                    })
553                }
554                ParameterValue::SubExpr(ref call) => {
555                    self.statement(call)?.unwrap_or_else(|| {
556                        missing.push(MissingValue::Argument(
557                            i,
558                            Value::String(call.as_str().to_string()),
559                        ));
560                        Value::Null
561                    })
562                }
563            };
564            out.push(arg);
565        }
566        Ok(out)
567    }
568
569    /// Create the context hash parameters.
570    fn hash(
571        &mut self,
572        call: &Call<'_>,
573        missing: &mut Vec<MissingValue>,
574    ) -> RenderResult<Map<String, Value>> {
575        let mut out = Map::new();
576        for (k, p) in call.parameters() {
577            let (key, value) = match p {
578                ParameterValue::Json { ref value, .. } => {
579                    (k.to_string(), value.clone())
580                }
581                ParameterValue::Path(ref path) => {
582                    let val = self.lookup(path).cloned().unwrap_or_else(|| {
583                        missing.push(MissingValue::Parameter(
584                            k.to_string(),
585                            Value::String(path.as_str().to_string()),
586                        ));
587                        Value::Null
588                    });
589                    (k.to_string(), val)
590                }
591                ParameterValue::SubExpr(ref call) => (
592                    k.to_string(),
593                    self.statement(call)?.unwrap_or_else(|| {
594                        missing.push(MissingValue::Parameter(
595                            k.to_string(),
596                            Value::String(call.as_str().to_string()),
597                        ));
598                        Value::Null
599                    }),
600                ),
601            };
602            out.insert(key, value);
603        }
604
605        Ok(out)
606    }
607
608    /// Register a local helper.
609    ///
610    /// Local helpers are available for the scope of the parent helper.
611    pub fn register_local_helper(
612        &mut self,
613        name: &'render str,
614        helper: Box<dyn LocalHelper + 'render>,
615    ) {
616        let registry = Rc::make_mut(&mut self.local_helpers);
617        registry.borrow_mut().insert(name.to_string(), helper);
618    }
619
620    /// Remove a local helper.
621    ///
622    /// Local helpers will be removed once a helper call has finished
623    /// but you can call this if you want to be explicit.
624    pub fn unregister_local_helper(&mut self, name: &'render str) {
625        let registry = Rc::make_mut(&mut self.local_helpers);
626        registry.borrow_mut().remove(name);
627    }
628
629    fn invoke<'a>(
630        &mut self,
631        name: &str,
632        target: HelperTarget<'a>,
633        call: &Call<'_>,
634        content: Option<&'render Node<'render>>,
635        text: Option<&'render str>,
636        property: Option<Property>,
637    ) -> RenderResult<HelperValue> {
638        let site = if content.is_some() {
639            CallSite::BlockHelper(name.to_string())
640        } else {
641            CallSite::Helper(name.to_string())
642        };
643
644        let amount = self.stack.iter().filter(|&n| *n == site).count();
645        if amount >= STACK_MAX {
646            return Err(RenderError::HelperCycle(site.into()));
647        }
648        self.stack.push(site);
649
650        let mut missing: Vec<MissingValue> = Vec::new();
651        let args = self.arguments(call, &mut missing)?;
652        let hash = self.hash(call, &mut missing)?;
653        let mut context = Context::new(
654            call,
655            name.to_owned(),
656            args,
657            hash,
658            text,
659            property,
660            missing,
661        );
662
663        let local_helpers = Rc::clone(&self.local_helpers);
664
665        let value: Option<Value> = match target {
666            HelperTarget::Name(name) => {
667                if let Some(helper) = local_helpers.borrow().get(name) {
668                    helper.call(self, &mut context, content)?
669                } else if let Some(helper) = self.registry.helpers().get(name) {
670                    helper.call(self, &mut context, content)?
671                } else {
672                    None
673                }
674            }
675            // NOTE: evnet handlers will pass a reference to the helper.
676            HelperTarget::Helper(helper) => {
677                helper.call(self, &mut context, content)?
678            }
679        };
680
681        drop(local_helpers);
682
683        self.stack.pop();
684
685        Ok(value)
686    }
687
688    fn has_helper(&mut self, name: &str) -> bool {
689        self.local_helpers.borrow().get(name).is_some()
690            || self.registry.helpers().get(name).is_some()
691    }
692
693    // Fallible version of path lookup.
694    fn resolve(&mut self, path: &Path<'_>) -> RenderResult<HelperValue> {
695        if let Some(value) = self.lookup(path).cloned().take() {
696            Ok(Some(value))
697        } else {
698            if self.registry.strict() {
699                Err(RenderError::VariableNotFound(
700                    path.as_str().to_string(),
701                    self.name.to_string(),
702                ))
703            } else {
704                // TODO: call a missing_variable handler?
705                Ok(None)
706            }
707        }
708    }
709
710    /// Invoke a call and return the result.
711    pub(crate) fn call(
712        &mut self,
713        call: &Call<'_>,
714    ) -> RenderResult<HelperValue> {
715        match call.target() {
716            CallTarget::Path(ref path) => {
717                // Explicit paths should resolve to a lookup
718                if path.is_explicit() {
719                    Ok(self.lookup(path).cloned())
720                // Simple paths may be helpers
721                } else if path.is_simple() {
722                    if self.has_helper(path.as_str()) {
723                        self.invoke(
724                            path.as_str(),
725                            HelperTarget::Name(path.as_str()),
726                            call,
727                            None,
728                            None,
729                            None,
730                        )
731                    } else {
732                        let value = self.lookup(path).cloned();
733                        if let None = value {
734                            if let Some(ref helper) =
735                                self.registry.handlers().helper_missing
736                            {
737                                return self.invoke(
738                                    HELPER_MISSING,
739                                    HelperTarget::Helper(helper),
740                                    call,
741                                    None,
742                                    None,
743                                    None,
744                                );
745                            } else {
746                                // TODO: also error if Call has arguments or parameters
747                                if self.registry.strict() {
748                                    return Err(RenderError::VariableNotFound(
749                                        path.as_str().to_string(),
750                                        self.name.to_string(),
751                                    ));
752                                }
753                            }
754                        }
755                        Ok(value)
756                    }
757                } else {
758                    self.resolve(path)
759                }
760            }
761            CallTarget::SubExpr(ref sub) => self.call(sub),
762        }
763    }
764
765    fn statement(&mut self, call: &Call<'_>) -> RenderResult<HelperValue> {
766        if call.is_partial() {
767            self.render_partial(call, None)?;
768            Ok(None)
769        } else {
770            Ok(self.call(call)?)
771        }
772    }
773
774    fn get_partial_name<'a>(
775        &mut self,
776        call: &Call<'_>,
777    ) -> RenderResult<String> {
778        match call.target() {
779            CallTarget::Path(ref path) => {
780                if path.as_str() == PARTIAL_BLOCK {
781                    return Ok(PARTIAL_BLOCK.to_string());
782                } else if path.is_simple() {
783                    return Ok(path.as_str().to_string());
784                } else {
785                    return Err(RenderError::PartialIdentifier(
786                        path.as_str().to_string(),
787                    ));
788                }
789            }
790            CallTarget::SubExpr(ref call) => {
791                let result = self.statement(call)?.unwrap_or(Value::Null);
792                return Ok(json::stringify(&result));
793            }
794        }
795    }
796
797    fn render_partial(
798        &mut self,
799        call: &Call<'_>,
800        partial_block: Option<&'render Node<'render>>,
801    ) -> RenderResult<()> {
802        let name = self.get_partial_name(call)?;
803
804        let site = CallSite::Partial(name.to_string());
805        if self.stack.contains(&site) {
806            return Err(RenderError::PartialCycle(site.into()));
807        }
808        self.stack.push(site);
809
810        if let Some(node) = partial_block {
811            self.partials.insert(PARTIAL_BLOCK.to_string(), node);
812        }
813
814        let node = if let Some(local_partial) = self.partials.get(&name) {
815            local_partial
816        } else {
817            let template = self
818                .get_template(&name)
819                .ok_or_else(|| RenderError::PartialNotFound(name))?;
820
821            self.current_partial_name.push(template.file_name());
822
823            template.node()
824        };
825
826        let mut missing: Vec<MissingValue> = Vec::new();
827        let hash = self.hash(call, &mut missing)?;
828        let scope = if !call.arguments().is_empty() {
829            let arguments = self.arguments(call, &mut missing)?;
830            if let Some(context) = arguments.get(0) {
831                Scope::from((context.clone(), hash))
832            } else {
833                Scope::from(hash)
834            }
835        } else {
836            Scope::from(hash)
837        };
838
839        self.scopes.push(scope);
840        // WARN: We must iterate the document child nodes
841        // WARN: when rendering partials otherwise the
842        // WARN: rendering process will halt after the first partial!
843        for event in node.into_iter().event(self.hint) {
844            self.render_node(event.node, event.trim)?;
845        }
846        self.scopes.pop();
847
848        self.current_partial_name.pop();
849        self.stack.pop();
850
851        Ok(())
852    }
853
854    fn block_helper_missing(
855        &mut self,
856        node: &'render Node<'render>,
857        _block: &'render Block<'render>,
858        call: &'render Call<'render>,
859        text: Option<&str>,
860        raw: bool,
861    ) -> RenderResult<()> {
862        // Handling a raw block without a corresponding helper
863        // so we just write out the content
864        if raw {
865            if let Some(text) = text {
866                self.write_str(text, false)?;
867            }
868        } else {
869            match call.target() {
870                CallTarget::Path(ref path) => {
871                    if let Some(value) = self.lookup(path).cloned() {
872                        if let Some(ref helper) =
873                            self.registry.handlers().block_helper_missing
874                        {
875                            let prop = Property {
876                                name: path.as_str().to_string(),
877                                value,
878                            };
879                            self.invoke(
880                                BLOCK_HELPER_MISSING,
881                                HelperTarget::Helper(helper),
882                                call,
883                                Some(node),
884                                None,
885                                Some(prop),
886                            )?;
887                        } else {
888                            // Default behavior is to just render the block
889                            self.template(node)?;
890                        }
891                    } else if let Some(ref helper) =
892                        self.registry.handlers().helper_missing
893                    {
894                        self.invoke(
895                            HELPER_MISSING,
896                            HelperTarget::Helper(helper),
897                            call,
898                            None,
899                            None,
900                            None,
901                        )?;
902                    } else {
903                        if self.registry.strict() {
904                            return Err(RenderError::HelperNotFound(
905                                path.as_str().to_string(),
906                            ));
907                        }
908                    }
909                }
910                _ => {}
911            }
912        }
913
914        Ok(())
915    }
916
917    fn block(
918        &mut self,
919        node: &'render Node<'render>,
920        block: &'render Block<'render>,
921    ) -> RenderResult<()> {
922        let call = block.call();
923        let raw = block.is_raw();
924
925        if call.is_partial() {
926            self.render_partial(call, Some(node))?;
927        } else {
928            match call.target() {
929                CallTarget::Path(ref path) => {
930                    if path.is_simple() {
931                        let mut text: Option<&str> = None;
932
933                        if raw {
934                            // Raw block nodes should have a single Text child node
935                            text = if !block.nodes().is_empty() {
936                                Some(block.nodes().get(0).unwrap().as_str())
937                            // Empty raw block should be treated as the empty string
938                            } else {
939                                Some("")
940                            };
941
942                            // Store the hint so we can remove leading whitespace
943                            // after a raw block end tag
944                            match node {
945                                Node::Block(ref block) => {
946                                    let hint = block.trim_close();
947
948                                    // Trim leading inside a raw block
949                                    if node.trim().after {
950                                        if let Some(ref content) = text {
951                                            text = Some(content.trim_start());
952                                        }
953                                    }
954
955                                    // Trim trailing inside a raw block
956                                    if hint.before {
957                                        if let Some(ref content) = text {
958                                            text = Some(content.trim_end());
959                                        }
960                                    }
961
962                                    // Trim after the end tag
963                                    self.end_tag_hint = Some(hint);
964                                }
965                                _ => {}
966                            }
967                        }
968
969                        if self.has_helper(path.as_str()) {
970                            self.invoke(
971                                path.as_str(),
972                                HelperTarget::Name(path.as_str()),
973                                call,
974                                Some(node),
975                                text,
976                                None,
977                            )?;
978                        } else {
979                            return self.block_helper_missing(
980                                node, block, call, text, raw,
981                            );
982                        }
983                    } else {
984                        return Err(RenderError::BlockIdentifier(
985                            path.as_str().to_string(),
986                        ));
987                    }
988                }
989                CallTarget::SubExpr(ref _call) => {
990                    return Err(RenderError::BlockTargetSubExpr)
991                }
992            }
993        }
994        Ok(())
995    }
996
997    // Try to call a link helper.
998    fn link(
999        &mut self,
1000        helper: &Box<dyn Helper + 'render>,
1001        link: &'render Link<'render>,
1002    ) -> RenderResult<()> {
1003        let lines = link.lines();
1004        let href = Value::String(link.href().to_string());
1005        let label = Value::String(link.label().to_string());
1006        let title = Value::String(link.title().to_string());
1007
1008        // Build a call so that the helper invocation flows
1009        // through the standard logic.
1010        let mut call = Call::new(link.source(), 0..0, 0..0);
1011        call.add_argument(ParameterValue::from((
1012            link.source(),
1013            href,
1014            link.href_span().clone(),
1015            lines.clone(),
1016        )));
1017        call.add_argument(ParameterValue::from((
1018            link.source(),
1019            label,
1020            link.label_span().clone(),
1021            lines.clone(),
1022        )));
1023        call.add_argument(ParameterValue::from((
1024            link.source(),
1025            title,
1026            link.title_span().clone(),
1027            lines.clone(),
1028        )));
1029
1030        self.invoke(
1031            HELPER_LINK,
1032            HelperTarget::Helper(helper),
1033            &call,
1034            None,
1035            None,
1036            None,
1037        )?;
1038
1039        Ok(())
1040    }
1041
1042    pub(crate) fn render_node(
1043        &mut self,
1044        node: &'render Node<'render>,
1045        trim: TrimState,
1046    ) -> RenderResult<()> {
1047        self.trim = trim;
1048        self.hint = Some(node.trim());
1049
1050        if let Some(hint) = self.end_tag_hint.take() {
1051            if hint.after {
1052                self.trim.start = true;
1053            }
1054        }
1055
1056        match node {
1057            Node::Text(ref n) => {
1058                self.write_str(n.as_str(), false)?;
1059            }
1060            Node::RawStatement(ref n) => {
1061                let raw = &n.as_str()[1..];
1062                self.write_str(raw, false)?;
1063            }
1064            Node::Link(ref n) => {
1065                if n.is_escaped() {
1066                    self.write_str(n.after_escape(), false)?;
1067                } else {
1068                    if cfg!(feature = "links") {
1069                        if let Some(helper) = &self.registry.handlers().link {
1070                            self.link(helper, n)?;
1071                        } else {
1072                            self.write_str(n.as_str(), false)?;
1073                        }
1074                    } else {
1075                        self.write_str(n.as_str(), false)?;
1076                    }
1077                }
1078            }
1079            Node::RawComment(_) => {}
1080            Node::Comment(_) => {}
1081            Node::Document(_) => {}
1082            Node::Statement(ref call) => {
1083                if let Some(ref value) = self.statement(call)? {
1084                    let val = json::stringify(value);
1085                    self.write_str(&val, call.is_escaped())?;
1086                }
1087            }
1088            Node::Block(ref block) => {
1089                self.block(node, block)?;
1090            }
1091        }
1092
1093        Ok(())
1094    }
1095
1096    fn write_str(&mut self, s: &str, escape: bool) -> RenderResult<usize> {
1097        let val = if self.trim.start { s.trim_start() } else { s };
1098        let val = if self.trim.end { val.trim_end() } else { val };
1099        if val.is_empty() {
1100            return Ok(0);
1101        }
1102
1103        if escape {
1104            let escaped = (self.registry.escape())(val);
1105            Ok(self.writer.write_str(&escaped).map_err(RenderError::from)?)
1106        } else {
1107            Ok(self.writer.write_str(val).map_err(RenderError::from)?)
1108        }
1109    }
1110}