Repository: Keats/tera (rust)

Issue:
Variable keys with forward slashes
I've got a hashmap with keys that contain slashes. I can see the context set as expected:

```
&context = Context {
    data: {
        "variables": Object(
            {
                "Test/Variable": String(
                    "test value",
                ),
            },
        ),
    },
}
```

But I can't seem to access the value using either `variables["Test/Variable"]` or `variables['Test/Variable']`. I couldn't find a test that covers this, and it's not explicitly mentioned in the documentation, but I assumed that the bracket-notation could be used specifically for this use case.

The returned error is:

> Variable `variables[\'Test/Variable\']` not found in context while rendering \'tpl\': the evaluated version was `variables.Test/Variable`. Maybe the index is out of bounds?

Looking at the source code, it seems that in the case of a string inside the brackets (so `["var"]`, as opposed to an actual variable with `[var]`), it simply converts it to a regular dot-notation syntax without any special processing:

https://github.com/Keats/tera/blob/2cb367877e296ff77f4b7bb7ee68a4196f426ecd/src/renderer/processor.rs#L61-L67

I feel like this just isn't possible. Should it be? Since it accepts a hashmap, and the context contains the data, it should be able to fetch the data even if the variable contains slashes.

I don't have control of the keys in the context, but I can of course do some pre-processing to convert them into variables that work with the dot-notation, but I was hoping this is (or can be) supported in Tera by default.

Pull request:
fix: escape slash in JSON path according to RFC6901
see: https://tools.ietf.org/html/rfc6901#section-3

closes #431.

Patch:
diff --git a/src/renderer/processor.rs b/src/renderer/processor.rs
index c7a6c5b..14f05cf 100644
--- a/src/renderer/processor.rs
+++ b/src/renderer/processor.rs
@@ -59,6 +59,7 @@ fn evaluate_sub_variables<'a>(key: &str, call_stack: &CallStack<'a>) -> Result<S
     }
 
     Ok(new_key
+        .replace("/", "~1") // https://tools.ietf.org/html/rfc6901#section-3
         .replace("['", ".")
         .replace("[\"", ".")
         .replace("[", ".")
@@ -595,7 +596,10 @@ impl<'a> Processor<'a> {
                 }
             }
             ExprVal::Ident(_) => {
-                let mut res = self.eval_expression(&bool_expr).unwrap_or_else(|_| Cow::Owned(Value::Bool(false))).is_truthy();
+                let mut res = self
+                    .eval_expression(&bool_expr)
+                    .unwrap_or_else(|_| Cow::Owned(Value::Bool(false)))
+                    .is_truthy();
                 if bool_expr.negated {
                     res = !res;
                 }


Test patch:
diff --git a/src/renderer/tests/square_brackets.rs b/src/renderer/tests/square_brackets.rs
index c1631f9..9696cec 100644
--- a/src/renderer/tests/square_brackets.rs
+++ b/src/renderer/tests/square_brackets.rs
@@ -23,8 +23,11 @@ fn var_access_by_square_brackets() {
     let mut map = HashMap::new();
     map.insert("true", "yes");
     map.insert("false", "no");
+    map.insert("with space", "works");
+    map.insert("with/slash", "works");
     let mut deep_map = HashMap::new();
     deep_map.insert("inner_map", &map);
+    context.insert("map", &map);
     context.insert("deep_map", &deep_map);
     context.insert("bool_vec", &vec!["true", "false"]);
 
@@ -35,6 +38,8 @@ fn var_access_by_square_brackets() {
         ("{{var['c'][0]}}", "fred"),
         ("{{var['c'][zero]}}", "fred"),
         ("{{var[a]}}", "i_am_actually_b"),
+        ("{{map['with space']}}", "works"),
+        ("{{map['with/slash']}}", "works"),
         ("{{deep_map['inner_map'][bool_vec[zero]]}}", "yes"),
     ];