Repository: sunng87/handlebars-rust (rust)

Issue:
@root support?
Howdy, 
Is this something easy to add? https://stackoverflow.com/questions/17364897/access-to-root-context-in-handlebar-js-template

In essence, using ``@root`` from any depth should return a reference to the root RenderContext variables. 

Also, is it really necessary to clone the variables when invoking a partial? Cloning the map is causing some major performance issues here, it's taking ~5 min to run.

Thanks!


Discussion:
Hi @hammett @root is not supported for now and I will definitely support it.

Currently I'm working on pest upgrade #177, which involves lots of changes so I will be working on this right after #177 is done.

For the second question I will look into it and come back to you this weekend.
Thanks @sunng87 I looked at the code, and thought that to implement both root and no clone, the RenderContext would have to have an Option<Rc<RenderContext>> back to the parent RC, but it touches way more than that, so I refrained from trying it for now. 

Patch:
diff --git a/src/context.rs b/src/context.rs
--- a/src/context.rs
+++ b/src/context.rs
@@ -33,6 +33,12 @@ fn parse_json_visitor_inner<'a>(
 
     let mut seg_stack: VecDeque<Pair<Rule, StrInput>> = VecDeque::new();
     for seg in parsed_path {
+        if seg.as_str() == "@root" {
+            seg_stack.clear();
+            path_stack.clear();
+            continue;
+        }
+
         match seg.as_rule() {
             Rule::path_up => {
                 path_stack.pop_back();
@@ -410,4 +416,23 @@ mod test {
         let d = UnserializableType {};
         assert!(Context::wraps(&d).is_err());
     }
+
+    #[test]
+    fn test_root() {
+        let m = json!({
+            "a" : {
+                "b" : {
+                    "c" : {
+                        "d" : 1
+                    }
+                }
+            },
+            "b": 2
+        });
+        let ctx = Context::wraps(&m).unwrap();
+        assert_eq!(ctx.navigate("a/b", &VecDeque::new(), "@root/b")
+                   .unwrap()
+                   .render(),
+                   "2".to_string());
+    }
 }
diff --git a/src/grammar.pest b/src/grammar.pest
--- a/src/grammar.pest
+++ b/src/grammar.pest
@@ -19,7 +19,7 @@ array_literal = { "[" ~ literal? ~ ("," ~ literal)* ~ "]" }
 object_literal = { "{" ~ (string_literal ~ ":" ~ literal)?
                    ~ ("," ~ string_literal ~ ":" ~ literal)* ~ "}" }
 
-symbol_char = _{'a'..'z'|'A'..'Z'|'0'..'9'|"-"|"_"|'\u{80}'..'\u{7ff}'|'\u{800}'..'\u{ffff}'|'\u{10000}'..'\u{10ffff}'}
+symbol_char = _{'a'..'z'|'A'..'Z'|'0'..'9'|"@"|"-"|"_"|'\u{80}'..'\u{7ff}'|'\u{800}'..'\u{ffff}'|'\u{10000}'..'\u{10ffff}'}
 path_char = _{ "/" }
 
 identifier = @{ symbol_char+ }


Test patch:
diff --git a/tests/root_var.rs b/tests/root_var.rs
new file mode 100644
--- /dev/null
+++ b/tests/root_var.rs
@@ -0,0 +1,18 @@
+extern crate handlebars;
+#[macro_use]
+extern crate serde_json;
+
+use handlebars::Handlebars;
+
+#[test]
+fn test_root_var() {
+    let hbs = Handlebars::new();
+
+    let data = json!({
+        "a": [1, 2, 3, 4],
+        "b": "top"
+    });
+
+    assert_eq!(hbs.template_render("{{#each a}}{{@root/b}}: {{this}};{{/each}}", &data).unwrap(),
+               "top: 1;top: 2;top: 3;top: 4;");
+}