mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 07:20:55 +00:00
Parse tuple expressions
Askama understands how to destructure tuples in let and match statements, but it does not understand how to build a tuple. This PR fixes this shortcoming.
This commit is contained in:
parent
85ad2e6ba3
commit
da0b6ead0e
@ -1061,6 +1061,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
||||
Expr::Call(ref obj, ref args) => self.visit_call(buf, obj, args)?,
|
||||
Expr::RustMacro(name, args) => self.visit_rust_macro(buf, name, args),
|
||||
Expr::Try(ref expr) => self.visit_try(buf, expr.as_ref())?,
|
||||
Expr::Tuple(ref exprs) => self.visit_tuple(buf, exprs)?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1403,6 +1404,23 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn visit_tuple(
|
||||
&mut self,
|
||||
buf: &mut Buffer,
|
||||
exprs: &[Expr<'_>],
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
buf.write("(");
|
||||
for (index, expr) in exprs.iter().enumerate() {
|
||||
if index > 0 {
|
||||
buf.write(" ");
|
||||
}
|
||||
self.visit_expr(buf, expr)?;
|
||||
buf.write(",");
|
||||
}
|
||||
buf.write(")");
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn visit_array(
|
||||
&mut self,
|
||||
buf: &mut Buffer,
|
||||
|
@ -61,6 +61,7 @@ pub enum Expr<'a> {
|
||||
BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
|
||||
Range(&'a str, Option<Box<Expr<'a>>>, Option<Box<Expr<'a>>>),
|
||||
Group(Box<Expr<'a>>),
|
||||
Tuple(Vec<Expr<'a>>),
|
||||
Call(Box<Expr<'a>>, Vec<Expr<'a>>),
|
||||
RustMacro(&'a str, &'a str),
|
||||
Try(Box<Expr<'a>>),
|
||||
@ -488,9 +489,31 @@ fn parameters(i: &str) -> IResult<&str, Vec<&str>> {
|
||||
}
|
||||
|
||||
fn expr_group(i: &str) -> IResult<&str, Expr<'_>> {
|
||||
map(delimited(ws(char('(')), expr_any, ws(char(')'))), |s| {
|
||||
Expr::Group(Box::new(s))
|
||||
})(i)
|
||||
let (i, expr) = preceded(ws(char('(')), opt(expr_any))(i)?;
|
||||
let expr = match expr {
|
||||
Some(expr) => expr,
|
||||
None => {
|
||||
let (i, _) = char(')')(i)?;
|
||||
return Ok((i, Expr::Tuple(vec![])));
|
||||
}
|
||||
};
|
||||
|
||||
let (i, comma) = ws(opt(peek(char(','))))(i)?;
|
||||
if comma.is_none() {
|
||||
let (i, _) = char(')')(i)?;
|
||||
return Ok((i, Expr::Group(Box::new(expr))));
|
||||
}
|
||||
|
||||
let mut exprs = vec![expr];
|
||||
let (i, _) = fold_many0(
|
||||
preceded(char(','), ws(expr_any)),
|
||||
|| (),
|
||||
|_, expr| {
|
||||
exprs.push(expr);
|
||||
},
|
||||
)(i)?;
|
||||
let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?;
|
||||
Ok((i, Expr::Tuple(exprs)))
|
||||
}
|
||||
|
||||
fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
|
||||
@ -1687,4 +1710,123 @@ mod tests {
|
||||
vec![Node::Comment(Ws(false, false))],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tuple() {
|
||||
use super::Expr::*;
|
||||
let syntax = Syntax::default();
|
||||
assert_eq!(
|
||||
super::parse("{{ () }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(Ws(false, false), Tuple(vec![]),)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(Ws(false, false), Group(Box::new(NumLit("1"))),)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1,) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1, ) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1 ,) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1 , ) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1, 2) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Tuple(vec![NumLit("1"), NumLit("2")]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1, 2,) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Tuple(vec![NumLit("1"), NumLit("2")]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1, 2, 3) }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ ()|abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Filter("abs", vec![Tuple(vec![])]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ () | abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1)|abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Filter("abs", vec![Group(Box::new(NumLit("1")))]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1) | abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
BinOp(
|
||||
"|",
|
||||
Box::new(Group(Box::new(NumLit("1")))),
|
||||
Box::new(Var("abs"))
|
||||
),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1,)|abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Filter("abs", vec![Tuple(vec![NumLit("1")])]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1,) | abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
BinOp(
|
||||
"|",
|
||||
Box::new(Tuple(vec![NumLit("1")])),
|
||||
Box::new(Var("abs"))
|
||||
),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1, 2)|abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse("{{ (1, 2) | abs }}", &syntax).unwrap(),
|
||||
vec![Node::Expr(
|
||||
Ws(false, false),
|
||||
BinOp(
|
||||
"|",
|
||||
Box::new(Tuple(vec![NumLit("1"), NumLit("2")])),
|
||||
Box::new(Var("abs"))
|
||||
),
|
||||
)],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
82
testing/tests/tuple.rs
Normal file
82
testing/tests/tuple.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use askama::Template;
|
||||
|
||||
struct Post {
|
||||
id: u32,
|
||||
}
|
||||
|
||||
struct Client<'a> {
|
||||
can_post_ids: &'a [u32],
|
||||
can_update_ids: &'a [u32],
|
||||
}
|
||||
|
||||
impl Client<'_> {
|
||||
fn can_post(&self, post: &Post) -> bool {
|
||||
self.can_post_ids.contains(&post.id)
|
||||
}
|
||||
|
||||
fn can_update(&self, post: &Post) -> bool {
|
||||
self.can_update_ids.contains(&post.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"
|
||||
{%- match (client.can_post(post), client.can_update(post)) -%}
|
||||
{%- when (false, false) -%}
|
||||
No!
|
||||
{%- when (can_post, can_update) -%}
|
||||
<ul>
|
||||
{%- if can_post -%}<li>post</li>{%- endif -%}
|
||||
{%- if can_update -%}<li>update</li>{%- endif -%}
|
||||
</ul>
|
||||
{%- endmatch -%}
|
||||
"#,
|
||||
ext = "txt"
|
||||
)]
|
||||
struct TupleTemplate<'a> {
|
||||
client: &'a Client<'a>,
|
||||
post: &'a Post,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple() {
|
||||
let template = TupleTemplate {
|
||||
client: &Client {
|
||||
can_post_ids: &[1, 2],
|
||||
can_update_ids: &[2, 3],
|
||||
},
|
||||
post: &Post { id: 1 },
|
||||
};
|
||||
assert_eq!(template.render().unwrap(), "<ul><li>post</li></ul>");
|
||||
|
||||
let template = TupleTemplate {
|
||||
client: &Client {
|
||||
can_post_ids: &[1, 2],
|
||||
can_update_ids: &[2, 3],
|
||||
},
|
||||
post: &Post { id: 2 },
|
||||
};
|
||||
assert_eq!(
|
||||
template.render().unwrap(),
|
||||
"<ul><li>post</li><li>update</li></ul>"
|
||||
);
|
||||
|
||||
let template = TupleTemplate {
|
||||
client: &Client {
|
||||
can_post_ids: &[1, 2],
|
||||
can_update_ids: &[2, 3],
|
||||
},
|
||||
post: &Post { id: 3 },
|
||||
};
|
||||
assert_eq!(template.render().unwrap(), "<ul><li>update</li></ul>");
|
||||
|
||||
let template = TupleTemplate {
|
||||
client: &Client {
|
||||
can_post_ids: &[1, 2],
|
||||
can_update_ids: &[2, 3],
|
||||
},
|
||||
post: &Post { id: 4 },
|
||||
};
|
||||
assert_eq!(template.render().unwrap(), "No!");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user