diff options
-rw-r--r-- | askama_shared/src/generator.rs | 39 | ||||
-rw-r--r-- | askama_shared/src/parser.rs | 66 | ||||
-rw-r--r-- | testing/tests/simple.rs | 37 |
3 files changed, 133 insertions, 9 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 59df687..fb8619e 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -909,7 +909,9 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { Expr::StrLit(s) => self.visit_str_lit(buf, s), Expr::CharLit(s) => self.visit_char_lit(buf, s), Expr::Var(s) => self.visit_var(buf, s), + Expr::VarCall(var, ref args) => self.visit_var_call(buf, var, args), Expr::Path(ref path) => self.visit_path(buf, path), + Expr::PathCall(ref path, ref args) => self.visit_path_call(buf, path, args), Expr::Array(ref elements) => self.visit_array(buf, elements), Expr::Attr(ref obj, name) => self.visit_attr(buf, obj, name), Expr::Index(ref obj, ref key) => self.visit_index(buf, obj, key), @@ -1160,6 +1162,24 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { DisplayWrap::Unwrapped } + fn visit_path_call(&mut self, buf: &mut Buffer, path: &[&str], args: &[Expr]) -> DisplayWrap { + for (i, part) in path.iter().enumerate() { + if i > 0 { + buf.write("::"); + } + buf.write(part); + } + buf.write("("); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + buf.write(","); + } + self.visit_expr(buf, arg); + } + buf.write(")"); + DisplayWrap::Unwrapped + } + fn visit_var(&mut self, buf: &mut Buffer, s: &str) -> DisplayWrap { if self.locals.contains(s) || s == "self" { buf.write(s); @@ -1170,6 +1190,25 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { DisplayWrap::Unwrapped } + fn visit_var_call(&mut self, buf: &mut Buffer, s: &str, args: &[Expr]) -> DisplayWrap { + buf.write("("); + if self.locals.contains(s) || s == "self" { + buf.write(s); + } else { + buf.write("self."); + buf.write(s); + } + buf.write(")("); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + buf.write(","); + } + self.visit_expr(buf, arg); + } + buf.write(")"); + DisplayWrap::Unwrapped + } + fn visit_bool_lit(&mut self, buf: &mut Buffer, s: &str) -> DisplayWrap { buf.write(s); DisplayWrap::Unwrapped diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index 9c3358e..b6404c5 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -10,14 +10,16 @@ use std::str; use crate::Syntax; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Expr<'a> { BoolLit(&'a str), NumLit(&'a str), StrLit(&'a str), CharLit(&'a str), Var(&'a str), + VarCall(&'a str, Vec<Expr<'a>>), Path(Vec<&'a str>), + PathCall(Vec<&'a str>, Vec<Expr<'a>>), Array(Vec<Expr<'a>>), Attr(Box<Expr<'a>>, &'a str), Index(Box<Expr<'a>>, Box<Expr<'a>>), @@ -30,7 +32,7 @@ pub enum Expr<'a> { RustMacro(&'a str, &'a str), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MatchVariant<'a> { Path(Vec<&'a str>), Name(&'a str), @@ -39,7 +41,7 @@ pub enum MatchVariant<'a> { CharLit(&'a str), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MatchParameter<'a> { Name(&'a str), NumLit(&'a str), @@ -47,16 +49,16 @@ pub enum MatchParameter<'a> { CharLit(&'a str), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Target<'a> { Name(&'a str), Tuple(Vec<&'a str>), } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct WS(pub bool, pub bool); -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Macro<'a> { pub ws1: WS, pub args: Vec<&'a str>, @@ -64,7 +66,7 @@ pub struct Macro<'a> { pub ws2: WS, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Node<'a> { Lit(&'a str, &'a str, &'a str), Comment(WS), @@ -84,6 +86,7 @@ pub enum Node<'a> { } pub type Cond<'a> = (WS, Option<Expr<'a>>, Vec<Node<'a>>); + pub type When<'a> = ( WS, Option<MatchVariant<'a>>, @@ -91,7 +94,7 @@ pub type When<'a> = ( Vec<Node<'a>>, ); -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MatchParameters<'a> { Simple(Vec<MatchParameter<'a>>), Named(Vec<(&'a str, Option<MatchParameter<'a>>)>), @@ -301,15 +304,30 @@ fn expr_var(i: &[u8]) -> IResult<&[u8], Expr> { map(identifier, |s| Expr::Var(s))(i) } -fn expr_path(i: &[u8]) -> IResult<&[u8], Expr> { +fn expr_var_call(i: &[u8]) -> IResult<&[u8], Expr> { + let (i, (s, args)) = tuple((identifier, arguments))(i)?; + Ok((i, Expr::VarCall(s, args))) +} + +fn path(i: &[u8]) -> IResult<&[u8], Vec<&str>> { let tail = separated_nonempty_list(tag("::"), identifier); let (i, (start, _, rest)) = tuple((identifier, tag("::"), tail))(i)?; let mut path = vec![start]; path.extend(rest); + Ok((i, path)) +} + +fn expr_path(i: &[u8]) -> IResult<&[u8], Expr> { + let (i, path) = path(i)?; Ok((i, Expr::Path(path))) } +fn expr_path_call(i: &[u8]) -> IResult<&[u8], Expr> { + let (i, (path, args)) = tuple((path, arguments))(i)?; + Ok((i, Expr::PathCall(path, args))) +} + fn variant_path(i: &[u8]) -> IResult<&[u8], MatchVariant> { map(separated_nonempty_list(tag("::"), identifier), |path| { MatchVariant::Path(path) @@ -437,9 +455,11 @@ fn expr_single(i: &[u8]) -> IResult<&[u8], Expr> { expr_num_lit, expr_str_lit, expr_char_lit, + expr_path_call, expr_path, expr_rust_macro, expr_array_lit, + expr_var_call, expr_var, expr_group, ))(i) @@ -1079,6 +1099,34 @@ mod tests { } #[test] + fn test_parse_var_call() { + assert_eq!( + super::parse("{{ function(\"123\", 3) }}", &Syntax::default()), + vec![super::Node::Expr( + super::WS(false, false), + super::Expr::VarCall( + "function", + vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")] + ), + )], + ); + } + + #[test] + fn test_parse_path_call() { + assert_eq!( + super::parse("{{ self::function(\"123\", 3) }}", &Syntax::default()), + vec![super::Node::Expr( + super::WS(false, false), + super::Expr::PathCall( + vec!["self", "function"], + vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")], + ), + )], + ); + } + + #[test] fn change_delimiters_parse_filter() { let syntax = Syntax { expr_start: "{~", diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs index 3544599..3e156b4 100644 --- a/testing/tests/simple.rs +++ b/testing/tests/simple.rs @@ -263,6 +263,43 @@ fn test_slice_literal() { } #[derive(Template)] +#[template(source = "Hello, {{ world(\"123\", 4) }}!", ext = "txt")] +struct FunctionRefTemplate { + world: fn(s: &str, v: u8) -> String, +} + +#[test] +fn test_func_ref_call() { + let t = FunctionRefTemplate { + world: |s, r| format!("world({}, {})", s, r), + }; + assert_eq!(t.render().unwrap(), "Hello, world(123, 4)!"); +} + +fn world2(s: &str, v: u8) -> String { + format!("world{}{}", v, s) +} + +#[derive(Template)] +#[template(source = "Hello, {{ self::world2(\"123\", 4) }}!", ext = "txt")] +struct PathFunctionTemplate; + +#[test] +fn test_path_func_call() { + assert_eq!(PathFunctionTemplate.render().unwrap(), "Hello, world4123!"); +} + +#[derive(Template)] +#[template(source = "Hello, {{ Self::world3(self, \"123\", 4) }}!", ext = "txt")] +struct FunctionTemplate; + +impl FunctionTemplate { + fn world3(&self, s: &str, v: u8) -> String { + format!("world{}{}", s, v) + } +} + +#[derive(Template)] #[template(source = " {# foo -#} ", ext = "txt")] struct CommentTemplate {} |