Repository: djc/askama (rust)

Issue:
Add support for reference operator (i.e. `{% let x = &y %}`)
Sometimes while working with deeply nested structures, it would be useful to create an alias to a field, like say `{% let h = &parent.child.hash %}`

At the moment the `&` unary operator is not supported.


Pull request description:
Add `as_ref` builtin
Fixes #330.

Following discussions with @djc, to keep `askama` template syntax as simple as possible and to go around the limitations described in #330, a good way to fix it is to add a `as_ref` builtin filter.

Patch:
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 0f9a2af..2040cac 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -1210,6 +1210,20 @@ impl<'a> Generator<'a> {
         Ok(DisplayWrap::Wrapped)
     }
 
+    fn _visit_as_ref_filter(
+        &mut self,
+        buf: &mut Buffer,
+        args: &[Expr<'_>],
+    ) -> Result<(), CompileError> {
+        let arg = match args {
+            [arg] => arg,
+            _ => return Err("unexpected argument(s) in `as_ref` filter".into()),
+        };
+        buf.write("&");
+        self.visit_expr(buf, arg)?;
+        Ok(())
+    }
+
     fn visit_filter(
         &mut self,
         buf: &mut Buffer,
@@ -1230,6 +1244,9 @@ impl<'a> Generator<'a> {
             return Ok(DisplayWrap::Unwrapped);
         } else if name == "markdown" {
             return self._visit_markdown_filter(buf, args);
+        } else if name == "as_ref" {
+            self._visit_as_ref_filter(buf, args)?;
+            return Ok(DisplayWrap::Wrapped);
         }
 
         if name == "tojson" {
diff --git a/book/src/filters.md b/book/src/filters.md
index e85068f..ea8198a 100644
--- a/book/src/filters.md
+++ b/book/src/filters.md
@@ -21,6 +21,7 @@ but are disabled by default. Enable them with Cargo features (see below for more
 
 * **[Built-in filters][#built-in-filters]:**  
   [`abs`][#abs],
+  [`as_ref`][#as_ref],
   [`capitalize`][#capitalize],
   [`center`][#center],
   [`escape|e`][#escape],
@@ -62,6 +63,23 @@ Output:
 2
 ```
 
+### as_ref
+[#as_ref]: #as_ref
+
+Creates a reference to the given argument.
+
+```
+{{ "a"|as_ref }}
+{{ self.x|as_ref }}
+```
+
+will become:
+
+```
+&a
+&self.x
+```
+
 ### capitalize
 [#capitalize]: #capitalize
 


Test patch:
diff --git a/testing/tests/filters.rs b/testing/tests/filters.rs
index 5a02909..4fe5c62 100644
--- a/testing/tests/filters.rs
+++ b/testing/tests/filters.rs
@@ -309,3 +309,17 @@ fn test_json_script() {
         r#"<script>var user = "\u003c/script\u003e\u003cbutton\u003eHacked!\u003c/button\u003e"</script>"#
     );
 }
+
+#[derive(askama::Template)]
+#[template(source = "{% let word = s|as_ref %}{{ word }}", ext = "html")]
+struct LetBorrow {
+    s: String,
+}
+
+#[test]
+fn test_let_borrow() {
+    let template = LetBorrow {
+        s: "hello".to_owned(),
+    };
+    assert_eq!(template.render().unwrap(), "hello")
+}