diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index eb293b0ac..3e4801f4f 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -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("("); diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs index 4da174252..ba117c57d 100644 --- a/askama_parser/src/node.rs +++ b/askama_parser/src/node.rs @@ -128,10 +128,23 @@ pub enum Target<'a> { CharLit(&'a str), BoolLit(&'a str), Path(Vec<&'a str>), + OrChain(Vec>), } 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), + |mut opts| match opts.len() { + 1 => opts.pop().unwrap(), + _ => Self::OrChain(opts), + }, + )(i) + } + + /// Parses a single target without an `or`, unless it is wrapped in parentheses. + 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()); diff --git a/testing/templates/match-enum-or.html b/testing/templates/match-enum-or.html new file mode 100644 index 000000000..ffd364db2 --- /dev/null +++ b/testing/templates/match-enum-or.html @@ -0,0 +1,8 @@ +The card is +{%- match suit %} + {%- when Suit::Clubs or Suit::Spades -%} + {{ " black" }} + {%- when Suit::Diamonds or Suit::Hearts -%} + {{ " red" }} +{%- endmatch %} + diff --git a/testing/tests/matches.rs b/testing/tests/matches.rs index f5ccb95e7..45074891c 100644 --- a/testing/tests/matches.rs +++ b/testing/tests/matches.rs @@ -195,3 +195,32 @@ 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-or.html")] +struct MatchEnumOrTemplate { + suit: Suit, +} + +#[test] +fn test_match_enum_or() { + let template = MatchEnumOrTemplate { suit: Suit::Clubs }; + assert_eq!(template.render().unwrap(), "The card is black\n"); + let template = MatchEnumOrTemplate { suit: Suit::Spades }; + assert_eq!(template.render().unwrap(), "The card is black\n"); + + let template = MatchEnumOrTemplate { suit: Suit::Hearts }; + assert_eq!(template.render().unwrap(), "The card is red\n"); + + let template = MatchEnumOrTemplate { + suit: Suit::Diamonds, + }; + assert_eq!(template.render().unwrap(), "The card is red\n"); +}