From 48c6cd327d3c1df4218898be509250efcc56597c Mon Sep 17 00:00:00 2001
From: PizzasBear <43722034+PizzasBear@users.noreply.github.com>
Date: Wed, 22 Nov 2023 15:56:14 +0200
Subject: Enhance match to include multiple targets (#911)

Signed-off-by: max <gmx.sht@gmail.com>
---
 askama_derive/src/generator.rs       | 10 ++++++++++
 askama_parser/src/node.rs            | 13 +++++++++++++
 testing/templates/match-enum-or.html |  8 ++++++++
 testing/tests/matches.rs             | 29 +++++++++++++++++++++++++++++
 4 files changed, 60 insertions(+)
 create mode 100644 testing/templates/match-enum-or.html

diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 8f0b7b3..c6c5d10 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -1579,6 +1579,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 4da1742..ba117c5 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<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),
+            |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 0000000..ffd364d
--- /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 f5ccb95..4507489 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");
+}
-- 
cgit