Repository: sunng87/handlebars-rust (rust)

Issue:
Generic parameter in `handlebars_helper` does not compile.
Because the `handlebars_helper` macro uses `$tt`, the macro fails to compile in the following case:

```rust
handlebars_helper!(date_helper: |dt: DateTime<Utc>| dt.format("%F"));
```

```
error: no rules expected the token `<`
   --> src/bin.rs:119:50
    |
119 |     handlebars_helper!(date_helper: |dt: DateTime<Utc>| dt.format("%R"));
    |                                                  ^ no rules expected this token in macro call
```

Pull request description:
Support generic parameters in handlebars_helper macro
This PR allows generic types in the simple parameter list of `handlebars_helper!`. For example, the following is now supported:

```rust
handlebars_helper!(date: |dt: DateTime<FixedOffset>| dt.format("%F").to_string());
```

This PR does not allow generic parameters in the hashmap syntax.

Fixes #460

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b1bbec..19eb85b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Change Log
 
+## [Unreleased]
+
+* [Added] Support for generic types in `handlebars_helper!`.
+
 ## [4.1.1](https://github.com/sunng87/handlebars-rust/compare/4.1.0...4.1.1) - 2021-07-31
 
 * [Changed] Update rhai to 1.0 [#455]
diff --git a/Cargo.toml b/Cargo.toml
index 594ca6a..0a143f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ tempfile = "3.0.0"
 criterion = "0.3"
 warp = "0.3"
 tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
+chrono = { version = "0.4.19", features = ["serde"] }
 
 [target.'cfg(unix)'.dev-dependencies]
 pprof = { version = "0.4", features = ["flamegraph", "protobuf"] }
diff --git a/src/macros.rs b/src/macros.rs
index 14cb015..bd1eab0 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -38,7 +38,7 @@
 
 #[macro_export]
 macro_rules! handlebars_helper {
-    ($struct_name:ident: |$($name:ident: $tpe:tt),*
+    ($struct_name:ident: |$($name:ident: $tpe:tt$(<$($gen:ty),+>)?),*
      $($(,)?{$($hash_name:ident: $hash_tpe:tt=$dft_val:literal),*})?
      $($(,)?*$args:ident)?
      $($(,)?**$kwargs:ident)?|
@@ -71,11 +71,11 @@ macro_rules! handlebars_helper {
                             stringify!($struct_name), stringify!($name),
                         )))
                         .and_then(|x|
-                                  handlebars_helper!(@as_json_value x, $tpe)
+                                  handlebars_helper!(@as_json_value x, $tpe$(<$($gen),+>)?)
                                   .ok_or_else(|| $crate::RenderError::new(&format!(
                                       "`{}` helper: Couldn't convert parameter {} to type `{}`. \
                                        It's {:?} as JSON. Got these params: {:?}",
-                                      stringify!($struct_name), stringify!($name), stringify!($tpe),
+                                      stringify!($struct_name), stringify!($name), stringify!($tpe$(<$($gen),+>)?),
                                       x, h.params(),
                                   )))
                         )?;
@@ -117,6 +117,7 @@ macro_rules! handlebars_helper {
     (@as_json_value $x:ident, bool) => { $x.as_bool() };
     (@as_json_value $x:ident, null) => { $x.as_null() };
     (@as_json_value $x:ident, Json) => { Some($x) };
+    (@as_json_value $x:ident, $tpe:tt$(<$($gen:ty),+>)?) => { serde_json::from_value::<$tpe$(<$($gen),+>)?>($x.clone()).ok() };
 }
 
 #[cfg(feature = "no_logging")]


Test patch:
diff --git a/tests/helper_macro.rs b/tests/helper_macro.rs
index 9a8d2dc..8df7378 100644
--- a/tests/helper_macro.rs
+++ b/tests/helper_macro.rs
@@ -3,6 +3,7 @@ extern crate handlebars;
 #[macro_use]
 extern crate serde_json;
 
+use chrono::{DateTime, FixedOffset};
 use handlebars::Handlebars;
 
 handlebars_helper!(lower: |s: str| s.to_lowercase());
@@ -14,6 +15,7 @@ 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));
+handlebars_helper!(date: |dt: DateTime<FixedOffset>| dt.format("%F").to_string());
 
 #[test]
 fn test_macro_helper() {
@@ -26,6 +28,7 @@ fn test_macro_helper() {
     hbs.register_helper("nargs", Box::new(nargs));
     hbs.register_helper("has_a", Box::new(has_a));
     hbs.register_helper("tag", Box::new(tag));
+    hbs.register_helper("date", Box::new(date));
 
     let data = json!("Teixeira");
 
@@ -73,4 +76,13 @@ fn test_macro_helper() {
         hbs.render_template("{{{tag \"html\"}}}", &()).unwrap(),
         "<html>"
     );
+
+    assert_eq!(
+        hbs.render_template(
+            "{{date this}}",
+            &DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap()
+        )
+        .unwrap(),
+        "2015-02-18"
+    );
 }