Skip to content

Commit

Permalink
Add ability to or patterns in any context (e.g. in when and let)
Browse files Browse the repository at this point in the history
Signed-off-by: max <gmx.sht@gmail.com>
  • Loading branch information
PizzasBear committed Nov 19, 2023
1 parent 6cbfde0 commit 43e21f8
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 6 deletions.
10 changes: 10 additions & 0 deletions askama_derive/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,16 @@ impl<'a> Generator<'a> {
}
buf.write(name);
}
Target::OrChain(targets) => match targets.first() {
None => buf.write("_"),
Some(first_target) => {
self.visit_target(buf, initialized, first_level, first_target);
for target in &targets[1..] {
buf.write(" | ");
self.visit_target(buf, initialized, first_level, target);
}
}
},
Target::Tuple(path, targets) => {
buf.write(&path.join("::"));
buf.write("(");
Expand Down
25 changes: 19 additions & 6 deletions askama_parser/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,26 @@ pub enum Target<'a> {
CharLit(&'a str),
BoolLit(&'a str),
Path(Vec<&'a str>),
OrChain(Vec<Target<'a>>),
}

impl<'a> Target<'a> {
/// Parses multiple targets with `or` separating them
pub(super) fn parse(i: &'a str) -> ParseResult<'a, Self> {
map(
separated_list1(ws(tag("or")), Self::parse_one),
|opts| match <[_; 1]>::try_from(opts) {
Ok([opt]) => opt,
Err(opts) => Self::OrChain(opts),
},
)(i)
}

/// Parses a single target without an `or`, unless it is wrapped in parentheses.
pub(super) fn parse_one(i: &'a str) -> ParseResult<'a, Self> {
let mut opt_opening_paren = map(opt(ws(char('('))), |o| o.is_some());
let mut opt_closing_paren = map(opt(ws(char(')'))), |o| o.is_some());
let mut opt_opening_brace = map(opt(ws(char('{'))), |o| o.is_some());
let mut opt_opening_brace = map(opt(ws(char('{' /* } */))), |o| o.is_some());

let (i, lit) = opt(Self::lit)(i)?;
if let Some(lit) = lit {
Expand Down Expand Up @@ -198,10 +211,10 @@ impl<'a> Target<'a> {
let (i, is_named_struct) = opt_opening_brace(i)?;
if is_named_struct {
let (i, targets) = alt((
map(char('}'), |_| Vec::new()),
map(char(/* { */ '}'), |_| Vec::new()),
terminated(
cut(separated_list1(ws(char(',')), Self::named)),
pair(opt(ws(char(','))), ws(cut(char('}')))),
pair(opt(ws(char(','))), ws(cut(char(/* { */ '}')))),
),
))(i)?;
return Ok((i, Self::Struct(path, targets)));
Expand Down Expand Up @@ -337,7 +350,7 @@ impl<'a> CondTest<'a> {
cut(tuple((
opt(delimited(
ws(alt((keyword("let"), keyword("set")))),
ws(Target::parse),
ws(Target::parse_one),
ws(char('=')),
)),
ws(|i| Expr::parse(i, s.level.get())),
Expand Down Expand Up @@ -410,7 +423,7 @@ impl<'a> Loop<'a> {
opt(Whitespace::parse),
ws(keyword("for")),
cut(tuple((
ws(Target::parse),
ws(Target::parse_one),
ws(keyword("in")),
cut(tuple((
ws(|i| Expr::parse(i, s.level.get())),
Expand Down Expand Up @@ -791,7 +804,7 @@ impl<'a> Let<'a> {
opt(Whitespace::parse),
ws(alt((keyword("let"), keyword("set")))),
cut(tuple((
ws(Target::parse),
ws(Target::parse_one),
opt(preceded(
ws(char('=')),
ws(|i| Expr::parse(i, s.level.get())),
Expand Down
8 changes: 8 additions & 0 deletions testing/templates/match-enum.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The card is
{%- match suit %}
{%- when Suit::Clubs or Suit::Spades -%}
{{ " black" }}
{%- when Suit::Diamonds or Suit::Hearts -%}
{{ " red" }}
{%- endmatch %}

22 changes: 22 additions & 0 deletions testing/tests/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,25 @@ fn test_match_with_comment() {
let s = MatchWithComment { good: false };
assert_eq!(s.render().unwrap(), "bad");
}

enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}

#[derive(Template)]
#[template(path = "match-enum.html")]
struct MatchEnumTemplate {
suit: Suit,
}

#[test]
fn test_match_enum() {
let s = MatchEnumTemplate { suit: Suit::Clubs };
assert_eq!(s.render().unwrap(), "The card is black\n");

let s = MatchEnumTemplate { suit: Suit::Hearts };
assert_eq!(s.render().unwrap(), "The card is red\n");
}

0 comments on commit 43e21f8

Please sign in to comment.