Repository: djc/askama (rust)

Issue:
Allow paths to start with `::`
[Discovered while working on #315 and #327.]

At the moment `{{ std::string::String::new()}}` works but `{{ ::std::string::String::new() }}` doesn't.


Pull request:
Allow paths to start with `::`
Fixes #331

I wanted to avoid changing `Path` and `PathCall` and the parameters of the visit functions. So instead I decided to just prepend `"::"` to the path parts `Vec`. If you'd rather have I add a `bool` to `Path` and `PathCall` I can do that instead.

_This does not introduce any breaking changes._








Patch:
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index 7224d02..8210ed0 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -1,7 +1,7 @@
 use nom::branch::alt;
 use nom::bytes::complete::{escaped, is_not, tag, take_until};
 use nom::character::complete::{anychar, char, digit1};
-use nom::combinator::{complete, map, opt};
+use nom::combinator::{complete, map, opt, value};
 use nom::error::ParseError;
 use nom::multi::{many0, many1, separated_list0, separated_list1};
 use nom::sequence::{delimited, pair, tuple};
@@ -312,10 +312,12 @@ fn expr_var_call(i: &[u8]) -> IResult<&[u8], Expr> {
 }
 
 fn path(i: &[u8]) -> IResult<&[u8], Vec<&str>> {
+    let root = opt(value("", ws(tag("::"))));
     let tail = separated_list1(ws(tag("::")), identifier);
-    let (i, (start, _, rest)) = tuple((identifier, ws(tag("::")), tail))(i)?;
-
-    let mut path = vec![start];
+    let (i, (root, start, _, rest)) = tuple((root, identifier, ws(tag("::")), tail))(i)?;
+    let mut path = Vec::new();
+    path.extend(root);
+    path.push(start);
     path.extend(rest);
     Ok((i, path))
 }
@@ -1136,6 +1138,25 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_parse_root_path() {
+        let syntax = Syntax::default();
+        assert_eq!(
+            super::parse("{{ std::string::String::new() }}", &syntax).unwrap(),
+            vec![Node::Expr(
+                WS(false, false),
+                Expr::PathCall(vec!["std", "string", "String", "new"], vec![]),
+            )],
+        );
+        assert_eq!(
+            super::parse("{{ ::std::string::String::new() }}", &syntax).unwrap(),
+            vec![Node::Expr(
+                WS(false, false),
+                Expr::PathCall(vec!["", "std", "string", "String", "new"], vec![]),
+            )],
+        );
+    }
+
     #[test]
     fn change_delimiters_parse_filter() {
         let syntax = Syntax {


Test patch:
diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs
index 7241005..5ef03b1 100644
--- a/testing/tests/simple.rs
+++ b/testing/tests/simple.rs
@@ -290,6 +290,15 @@ fn test_path_func_call() {
     assert_eq!(PathFunctionTemplate.render().unwrap(), "Hello, world4123!");
 }
 
+#[derive(Template)]
+#[template(source = "{{ ::std::string::ToString::to_string(123) }}", ext = "txt")]
+struct RootPathFunctionTemplate;
+
+#[test]
+fn test_root_path_func_call() {
+    assert_eq!(RootPathFunctionTemplate.render().unwrap(), "123");
+}
+
 #[derive(Template)]
 #[template(source = "Hello, {{ Self::world3(self, \"123\", 4) }}!", ext = "txt")]
 struct FunctionTemplate;