Repository: sunng87/handlebars-rust (rust)

Issue:
compatibility: escape functions are not applied to template tags with helper function
For example, using this helper function
```rust
handlebars_helper!(to_tag: |x: str| { format!("<{}>", x) });
```
render this template
```handlebars
{{to_tag tag}}
```
with value `tag = "br"` and registering `html_escape`, you will get output `<br>` instead of  `&lt;br&gt;` that original handlebars.js does. Using "unescaped" tag (`{{{to-tag tag}}}`) results in `TemplateError`.

FYI: I accidentally found workaround. Using parentheses like `{{(to-tag tag)}}` solved my problem. `{{(to-tag tag)}}` and `{{{(to-tag tag)}}}` resulting in the rendered text `&lt;br&gt;` and `<br>` respectively. But these templates seem to result in error in the original JS version.

Pull request description:
Escaping helper expression results
Fixes #361 

This patch partially fixed issue as mentioned in #361 that result of helper call should be escaped by default.

Further support for `{{{html expression}}}` that doesn't escape the result will be supported in 4.0

Patch:
diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs
index 744bba4..8d1c2d7 100644
--- a/src/helpers/mod.rs
+++ b/src/helpers/mod.rs
@@ -3,7 +3,7 @@ use crate::error::RenderError;
 use crate::json::value::ScopedJson;
 use crate::output::Output;
 use crate::registry::Registry;
-use crate::render::{Helper, RenderContext};
+use crate::render::{do_escape, Helper, RenderContext};
 
 pub use self::helper_each::EACH_HELPER;
 pub use self::helper_if::{IF_HELPER, UNLESS_HELPER};
@@ -98,7 +98,9 @@ pub trait HelperDef {
             if r.strict_mode() && result.is_missing() {
                 return Err(RenderError::strict_error(None));
             } else {
-                out.write(result.render().as_ref())?;
+                // auto escape according to settings
+                let output = do_escape(r, rc, result.render());
+                out.write(output.as_ref())?;
             }
         }
 
diff --git a/src/render.rs b/src/render.rs
index d2555b5..b49310f 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -710,6 +710,14 @@ fn render_helper<'reg: 'rc, 'rc>(
     }
 }
 
+pub(crate) fn do_escape(r: &Registry<'_>, rc: &RenderContext<'_, '_>, content: String) -> String {
+    if !rc.is_disable_escape() {
+        r.get_escape_fn()(&content)
+    } else {
+        content
+    }
+}
+
 impl Renderable for TemplateElement {
     fn render<'reg: 'rc, 'rc>(
         &'reg self,
@@ -746,11 +754,7 @@ impl Renderable for TemplateElement {
                             }
                         } else {
                             let rendered = context_json.value().render();
-                            let output = if !rc.is_disable_escape() {
-                                registry.get_escape_fn()(&rendered)
-                            } else {
-                                rendered
-                            };
+                            let output = do_escape(registry, rc, rendered);
                             out.write(output.as_ref())?;
                             Ok(())
                         }


Test patch:
diff --git a/tests/helper_macro.rs b/tests/helper_macro.rs
index 5aea882..9a845bb 100644
--- a/tests/helper_macro.rs
+++ b/tests/helper_macro.rs
@@ -13,6 +13,7 @@ handlebars_helper!(all_hash: |{cur: str="$"}| cur);
 handlebars_helper!(nargs: |*args| args.len());
 handlebars_helper!(has_a: |{a:i64 = 99}, **kwargs|
                    format!("{}, {}", a, kwargs.get("a").is_some()));
+handlebars_helper!(tag: |t: str| format!("<{}>", t));
 
 #[test]
 fn test_macro_helper() {
@@ -24,6 +25,7 @@ fn test_macro_helper() {
     hbs.register_helper("money", Box::new(money));
     hbs.register_helper("nargs", Box::new(nargs));
     hbs.register_helper("has_a", Box::new(has_a));
+    hbs.register_helper("tag", Box::new(tag));
 
     let data = json!("Teixeira");
 
@@ -61,4 +63,9 @@ fn test_macro_helper() {
         hbs.render_template("{{has_a x=1 b=2}}", &()).unwrap(),
         "99, false"
     );
+
+    assert_eq!(
+        hbs.render_template("{{tag \"html\"}}", &()).unwrap(),
+        "&lt;html&gt;"
+    );
 }