mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-29 22:11:17 +00:00
Fix Rust macro invocations not accepting a path (#837)
This commit is contained in:
parent
31e9ed52be
commit
ac8de6260e
@ -1373,7 +1373,7 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
Expr::Group(ref inner) => self.visit_group(buf, inner)?,
|
Expr::Group(ref inner) => self.visit_group(buf, inner)?,
|
||||||
Expr::Call(ref obj, ref args) => self.visit_call(buf, obj, args)?,
|
Expr::Call(ref obj, ref args) => self.visit_call(buf, obj, args)?,
|
||||||
Expr::RustMacro(name, args) => self.visit_rust_macro(buf, name, args),
|
Expr::RustMacro(ref path, args) => self.visit_rust_macro(buf, path, args),
|
||||||
Expr::Try(ref expr) => self.visit_try(buf, expr.as_ref())?,
|
Expr::Try(ref expr) => self.visit_try(buf, expr.as_ref())?,
|
||||||
Expr::Tuple(ref exprs) => self.visit_tuple(buf, exprs)?,
|
Expr::Tuple(ref exprs) => self.visit_tuple(buf, exprs)?,
|
||||||
})
|
})
|
||||||
@ -1390,8 +1390,8 @@ impl<'a> Generator<'a> {
|
|||||||
Ok(DisplayWrap::Unwrapped)
|
Ok(DisplayWrap::Unwrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_rust_macro(&mut self, buf: &mut Buffer, name: &str, args: &str) -> DisplayWrap {
|
fn visit_rust_macro(&mut self, buf: &mut Buffer, path: &[&str], args: &str) -> DisplayWrap {
|
||||||
buf.write(name);
|
self.visit_path(buf, path);
|
||||||
buf.write("!(");
|
buf.write("!(");
|
||||||
buf.write(args);
|
buf.write(args);
|
||||||
buf.write(")");
|
buf.write(")");
|
||||||
|
@ -4,9 +4,10 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::{tag, take_till};
|
use nom::bytes::complete::{tag, take_till};
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::combinator::{cut, map, not, opt, peek, recognize};
|
use nom::combinator::{cut, map, not, opt, peek, recognize};
|
||||||
|
use nom::error::ErrorKind;
|
||||||
use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
|
use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
|
||||||
use nom::sequence::{delimited, pair, preceded, terminated, tuple};
|
use nom::sequence::{delimited, pair, preceded, terminated, tuple};
|
||||||
use nom::IResult;
|
use nom::{error_position, IResult};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
bool_lit, char_lit, identifier, nested_parenthesis, not_ws, num_lit, path, str_lit, ws,
|
bool_lit, char_lit, identifier, nested_parenthesis, not_ws, num_lit, path, str_lit, ws,
|
||||||
@ -30,7 +31,7 @@ pub(crate) enum Expr<'a> {
|
|||||||
Group(Box<Expr<'a>>),
|
Group(Box<Expr<'a>>),
|
||||||
Tuple(Vec<Expr<'a>>),
|
Tuple(Vec<Expr<'a>>),
|
||||||
Call(Box<Expr<'a>>, Vec<Expr<'a>>),
|
Call(Box<Expr<'a>>, Vec<Expr<'a>>),
|
||||||
RustMacro(&'a str, &'a str),
|
RustMacro(Vec<&'a str>, &'a str),
|
||||||
Try(Box<Expr<'a>>),
|
Try(Box<Expr<'a>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +184,6 @@ fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
|
|||||||
expr_str_lit,
|
expr_str_lit,
|
||||||
expr_char_lit,
|
expr_char_lit,
|
||||||
expr_path,
|
expr_path,
|
||||||
expr_rust_macro,
|
|
||||||
expr_array_lit,
|
expr_array_lit,
|
||||||
expr_var,
|
expr_var,
|
||||||
expr_group,
|
expr_group,
|
||||||
@ -194,6 +194,8 @@ enum Suffix<'a> {
|
|||||||
Attr(&'a str),
|
Attr(&'a str),
|
||||||
Index(Expr<'a>),
|
Index(Expr<'a>),
|
||||||
Call(Vec<Expr<'a>>),
|
Call(Vec<Expr<'a>>),
|
||||||
|
// The value is the arguments of the macro call.
|
||||||
|
MacroCall(&'a str),
|
||||||
Try,
|
Try,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +220,16 @@ fn expr_call(i: &str) -> IResult<&str, Suffix<'_>> {
|
|||||||
map(arguments, Suffix::Call)(i)
|
map(arguments, Suffix::Call)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expr_macro(i: &str) -> IResult<&str, Suffix<'_>> {
|
||||||
|
preceded(
|
||||||
|
pair(ws(char('!')), char('(')),
|
||||||
|
cut(terminated(
|
||||||
|
map(recognize(nested_parenthesis), Suffix::MacroCall),
|
||||||
|
char(')'),
|
||||||
|
)),
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
fn expr_try(i: &str) -> IResult<&str, Suffix<'_>> {
|
fn expr_try(i: &str) -> IResult<&str, Suffix<'_>> {
|
||||||
map(preceded(take_till(not_ws), char('?')), |_| Suffix::Try)(i)
|
map(preceded(take_till(not_ws), char('?')), |_| Suffix::Try)(i)
|
||||||
}
|
}
|
||||||
@ -256,28 +268,26 @@ fn expr_prefix(i: &str) -> IResult<&str, Expr<'_>> {
|
|||||||
fn expr_suffix(i: &str) -> IResult<&str, Expr<'_>> {
|
fn expr_suffix(i: &str) -> IResult<&str, Expr<'_>> {
|
||||||
let (mut i, mut expr) = expr_single(i)?;
|
let (mut i, mut expr) = expr_single(i)?;
|
||||||
loop {
|
loop {
|
||||||
let (j, suffix) = opt(alt((expr_attr, expr_index, expr_call, expr_try)))(i)?;
|
let (j, suffix) = opt(alt((
|
||||||
i = j;
|
expr_attr, expr_index, expr_call, expr_try, expr_macro,
|
||||||
|
)))(i)?;
|
||||||
match suffix {
|
match suffix {
|
||||||
Some(Suffix::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
|
Some(Suffix::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
|
||||||
Some(Suffix::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
|
Some(Suffix::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
|
||||||
Some(Suffix::Call(args)) => expr = Expr::Call(expr.into(), args),
|
Some(Suffix::Call(args)) => expr = Expr::Call(expr.into(), args),
|
||||||
Some(Suffix::Try) => expr = Expr::Try(expr.into()),
|
Some(Suffix::Try) => expr = Expr::Try(expr.into()),
|
||||||
|
Some(Suffix::MacroCall(args)) => match expr {
|
||||||
|
Expr::Path(path) => expr = Expr::RustMacro(path, args),
|
||||||
|
Expr::Var(name) => expr = Expr::RustMacro(vec![name], args),
|
||||||
|
_ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))),
|
||||||
|
},
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
|
i = j;
|
||||||
}
|
}
|
||||||
Ok((i, expr))
|
Ok((i, expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn macro_arguments(i: &str) -> IResult<&str, &str> {
|
|
||||||
delimited(char('('), recognize(nested_parenthesis), char(')'))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_rust_macro(i: &str) -> IResult<&str, Expr<'_>> {
|
|
||||||
let (i, (mname, _, args)) = tuple((identifier, char('!'), macro_arguments))(i)?;
|
|
||||||
Ok((i, Expr::RustMacro(mname, args)))
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! expr_prec_layer {
|
macro_rules! expr_prec_layer {
|
||||||
( $name:ident, $inner:ident, $op:expr ) => {
|
( $name:ident, $inner:ident, $op:expr ) => {
|
||||||
fn $name(i: &str) -> IResult<&str, Expr<'_>> {
|
fn $name(i: &str) -> IResult<&str, Expr<'_>> {
|
||||||
|
@ -221,6 +221,51 @@ fn test_parse_root_path() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rust_macro() {
|
||||||
|
let syntax = Syntax::default();
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{ vec!(1, 2, 3) }}", &syntax).unwrap(),
|
||||||
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
Expr::RustMacro(vec!["vec"], "1, 2, 3",),
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{ alloc::vec!(1, 2, 3) }}", &syntax).unwrap(),
|
||||||
|
vec![Node::Expr(
|
||||||
|
Ws(None, None),
|
||||||
|
Expr::RustMacro(vec!["alloc", "vec"], "1, 2, 3",),
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{a!()}}", &syntax).unwrap(),
|
||||||
|
[Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{a !()}}", &syntax).unwrap(),
|
||||||
|
[Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{a! ()}}", &syntax).unwrap(),
|
||||||
|
[Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{a ! ()}}", &syntax).unwrap(),
|
||||||
|
[Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::parse("{{A!()}}", &syntax).unwrap(),
|
||||||
|
[Node::Expr(Ws(None, None), Expr::RustMacro(vec!["A"], ""),)],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&*super::parse("{{a.b.c!( hello )}}", &syntax)
|
||||||
|
.unwrap_err()
|
||||||
|
.msg,
|
||||||
|
"problems parsing template source at row 1, column 7 near:\n\"!( hello )}}\"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_delimiters_parse_filter() {
|
fn change_delimiters_parse_filter() {
|
||||||
let syntax = Syntax {
|
let syntax = Syntax {
|
||||||
|
1
testing/templates/rust-macros-full-path.html
Normal file
1
testing/templates/rust-macros-full-path.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello, {{ foo::hello2!() }}!
|
@ -11,11 +11,31 @@ macro_rules! hello {
|
|||||||
struct RustMacrosTemplate {}
|
struct RustMacrosTemplate {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn main() {
|
fn macro_basic() {
|
||||||
let template = RustMacrosTemplate {};
|
let template = RustMacrosTemplate {};
|
||||||
assert_eq!("Hello, world!", template.render().unwrap());
|
assert_eq!("Hello, world!", template.render().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod foo {
|
||||||
|
macro_rules! hello2 {
|
||||||
|
() => {
|
||||||
|
"world"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use hello2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "rust-macros-full-path.html")]
|
||||||
|
struct RustMacrosFullPathTemplate {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_full_path() {
|
||||||
|
let template = RustMacrosFullPathTemplate {};
|
||||||
|
assert_eq!("Hello, world!", template.render().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! call_a_or_b_on_tail {
|
macro_rules! call_a_or_b_on_tail {
|
||||||
((a: $a:expr, b: $b:expr, c: $c:expr), call a: $($tail:expr),*) => {
|
((a: $a:expr, b: $b:expr, c: $c:expr), call a: $($tail:expr),*) => {
|
||||||
($a)($($tail),*)
|
($a)($($tail),*)
|
||||||
@ -47,7 +67,7 @@ fn day(_: u16, _: &str, d: u8) -> u8 {
|
|||||||
struct RustMacrosArgTemplate {}
|
struct RustMacrosArgTemplate {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn args() {
|
fn macro_with_args() {
|
||||||
let template = RustMacrosArgTemplate {};
|
let template = RustMacrosArgTemplate {};
|
||||||
assert_eq!("2021\nJuly\n2", template.render().unwrap());
|
assert_eq!("2021\nJuly\n2", template.render().unwrap());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user