Repository: sunng87/handlebars-rust (rust)

Issue:
Attach custom data to Helper.
Hi!

I'm new to both rust and handlebar-rust, so I'm not quite sure if this is even possible.
Say I want to create a helper that references something else:

```rust
pub struct FooHelper<'a> {
    foo: &'a Foo,
}

let foo = Foo{}
handlebars.register_helper("foo_helper", Box::new(FooHelper{foo: &foo}));
```

This won't work as `register_helper` needs the helper to have `'static` lifetime. Does this mean that I can not attach a reference to the helper? Or maybe this is how it is intended to avoid some behaviors?

Thanks in advance!





Discussion:
Yes, the `'static` lifetime we were using doesn't allow borrowed runtime data in helper. I will look into if it's possible to use a proper lifetime for this case. Ideally we should be able to use borrowed data with lifetime equals to the registry.

Patch:
diff --git a/src/registry.rs b/src/registry.rs
--- a/src/registry.rs
+++ b/src/registry.rs
@@ -57,16 +57,16 @@ pub fn no_escape(data: &str) -> String {
 /// The single entry point of your Handlebars templates
 ///
 /// It maintains compiled templates and registered helpers.
-pub struct Registry {
+pub struct Registry<'reg> {
     templates: HashMap<String, Template>,
-    helpers: HashMap<String, Box<dyn HelperDef + 'static>>,
-    directives: HashMap<String, Box<dyn DirectiveDef + 'static>>,
+    helpers: HashMap<String, Box<dyn HelperDef + 'reg>>,
+    directives: HashMap<String, Box<dyn DirectiveDef + 'reg>>,
     escape_fn: EscapeFn,
     source_map: bool,
     strict_mode: bool,
 }
 
-impl Debug for Registry {
+impl<'reg> Debug for Registry<'reg> {
     fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
         f.debug_struct("Handlebars")
             .field("templates", &self.templates)
@@ -77,7 +77,7 @@ impl Debug for Registry {
     }
 }
 
-impl Default for Registry {
+impl<'reg> Default for Registry<'reg> {
     fn default() -> Self {
         Self::new()
     }
@@ -98,8 +98,8 @@ fn filter_file(entry: &DirEntry, suffix: &str) -> bool {
             .unwrap_or(true)
 }
 
-impl Registry {
-    pub fn new() -> Registry {
+impl<'reg> Registry<'reg> {
+    pub fn new() -> Registry<'reg> {
         let r = Registry {
             templates: HashMap::new(),
             helpers: HashMap::new(),
@@ -112,7 +112,7 @@ impl Registry {
         r.setup_builtins()
     }
 
-    fn setup_builtins(mut self) -> Registry {
+    fn setup_builtins(mut self) -> Registry<'reg> {
         self.register_helper("if", Box::new(helpers::IF_HELPER));
         self.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
         self.register_helper("each", Box::new(helpers::EACH_HELPER));
@@ -280,8 +280,8 @@ impl Registry {
     pub fn register_helper(
         &mut self,
         name: &str,
-        def: Box<dyn HelperDef + 'static>,
-    ) -> Option<Box<dyn HelperDef + 'static>> {
+        def: Box<dyn HelperDef + 'reg>,
+    ) -> Option<Box<dyn HelperDef + 'reg>> {
         self.helpers.insert(name.to_string(), def)
     }
 
@@ -289,8 +289,8 @@ impl Registry {
     pub fn register_decorator(
         &mut self,
         name: &str,
-        def: Box<dyn DirectiveDef + 'static>,
-    ) -> Option<Box<dyn DirectiveDef + 'static>> {
+        def: Box<dyn DirectiveDef + 'reg>,
+    ) -> Option<Box<dyn DirectiveDef + 'reg>> {
         self.directives.insert(name.to_string(), def)
     }
 
@@ -323,12 +323,12 @@ impl Registry {
     }
 
     /// Return a registered helper
-    pub fn get_helper(&self, name: &str) -> Option<&(dyn HelperDef + 'static)> {
+    pub fn get_helper(&self, name: &str) -> Option<&(dyn HelperDef)> {
         self.helpers.get(name).map(|v| v.as_ref())
     }
 
     /// Return a registered directive, aka decorator
-    pub fn get_decorator(&self, name: &str) -> Option<&(dyn DirectiveDef + 'static)> {
+    pub fn get_decorator(&self, name: &str) -> Option<&(dyn DirectiveDef)> {
         self.directives.get(name).map(|v| v.as_ref())
     }
 


Test patch:
diff --git a/tests/data_helper.rs b/tests/data_helper.rs
new file mode 100644
--- /dev/null
+++ b/tests/data_helper.rs
@@ -0,0 +1,29 @@
+use handlebars::*;
+use serde_json::json;
+
+struct HelperWithBorrowedData<'a>(&'a String);
+
+impl<'a> HelperDef for HelperWithBorrowedData<'a> {
+    fn call<'_reg: '_rc, '_rc>(
+        &self,
+        _: &Helper<'_reg, '_rc>,
+        _: &'_reg Handlebars,
+        _: &Context,
+        _: &mut RenderContext<'_reg>,
+        out: &mut dyn Output,
+    ) -> Result<(), RenderError> {
+        out.write(self.0).map_err(RenderError::from)
+    }
+}
+
+#[test]
+fn test_helper_with_ref_data() {
+    let s = "hello helper".to_owned();
+    let the_helper = HelperWithBorrowedData(&s);
+
+    let mut r = Handlebars::new();
+    r.register_helper("hello", Box::new(the_helper));
+
+    let s = r.render_template("Output: {{hello}}", &json!({})).unwrap();
+    assert_eq!(s, "Output: hello helper".to_owned());
+}