Repository: djc/askama
Pull request #182 (2018-12-12 19:51:39)

Allow using brackets for enums in `match`


Patch:
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index b30b040..047e975 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -1,6 +1,6 @@
 use super::{get_template_source, Context, Heritage};
 use crate::input::TemplateInput;
-use crate::parser::{Cond, Expr, MatchParameter, MatchVariant, Node, Target, When, WS};
+use crate::parser::{Cond, Expr, MatchParameter, MatchParameters, MatchVariant, Node, Target, When, WS};
 use askama_shared::filters;
 
 use proc_macro2::Span;
@@ -399,18 +399,43 @@ impl<'a> Generator<'a> {
                 }
                 None => buf.write("_"),
             };
-            if !params.is_empty() {
-                buf.write("(");
-                for (i, param) in params.iter().enumerate() {
-                    if let MatchParameter::Name(p) = *param {
-                        self.locals.insert(p);
+
+            match params {
+                MatchParameters::Simple(params) => {
+                    if !params.is_empty() {
+                        buf.write("(");
+                        for (i, param) in params.iter().enumerate() {
+                            if let MatchParameter::Name(p) = *param {
+                                self.locals.insert(p);
+                            }
+                            if i > 0 {
+                                buf.write(", ");
+                            }
+                            self.visit_match_param(buf, param);
+                        }
+                        buf.write(")");
                     }
-                    if i > 0 {
-                        buf.write(", ");
+                }
+                MatchParameters::Named(params) => {
+                    buf.write("{");
+                    for (i, param) in params.iter().enumerate() {
+                        if let Some(MatchParameter::Name(p)) = param.1 {
+                            self.locals.insert(p);
+                        } else {
+                            self.locals.insert(param.0);
+                        }
+
+                        if i > 0 {
+                            buf.write(", ");
+                        }
+                        buf.write(param.0);
+                        if let Some(param) = &param.1 {
+                            buf.write(":");
+                            self.visit_match_param(buf, &param);
+                        }
                     }
-                    self.visit_match_param(buf, param);
+                    buf.write("}");
                 }
-                buf.write(")");
             }
             buf.writeln(" => {");
             self.handle_ws(ws);
diff --git a/askama_derive/src/parser.rs b/askama_derive/src/parser.rs
index e1a7fc0..7968ac1 100644
--- a/askama_derive/src/parser.rs
+++ b/askama_derive/src/parser.rs
@@ -77,10 +77,22 @@ pub type Cond<'a> = (WS, Option<Expr<'a>>, Vec<Node<'a>>);
 pub type When<'a> = (
     WS,
     Option<MatchVariant<'a>>,
-    Vec<MatchParameter<'a>>,
+    MatchParameters<'a>,
     Vec<Node<'a>>,
 );
 
+#[derive(Debug)]
+pub enum MatchParameters<'a> {
+    Simple(Vec<MatchParameter<'a>>),
+    Named(Vec<(&'a str, Option<MatchParameter<'a>>)>),
+}
+
+impl<'a> Default for MatchParameters<'a> {
+    fn default() -> Self {
+        MatchParameters::Simple(vec![])
+    }
+}
+
 type Input<'a> = nom::types::CompleteByteSlice<'a>;
 #[allow(non_snake_case)]
 fn Input(input: &[u8]) -> Input {
@@ -309,8 +321,13 @@ named!(parameters<Input, Vec<&str>>, do_parse!(
     (vals.unwrap_or_default())
 ));
 
-named!(with_parameters<Input, Vec<MatchParameter>>, do_parse!(
+named!(with_parameters<Input, MatchParameters>, do_parse!(
     tag!("with") >>
+    value: alt!(match_simple_parameters | match_named_parameters) >>
+    (value)
+));
+
+named!(match_simple_parameters<Input, MatchParameters>, do_parse!(
     ws!(tag!("(")) >>
     vals: opt!(do_parse!(
         arg0: ws!(match_parameter) >>
@@ -326,7 +343,26 @@ named!(with_parameters<Input, Vec<MatchParameter>>, do_parse!(
         })
     )) >>
     tag!(")") >>
-    (vals.unwrap_or_default())
+    (MatchParameters::Simple(vals.unwrap_or_default()))
+));
+
+named!(match_named_parameters<Input, MatchParameters>, do_parse!(
+    ws!(tag!("{")) >>
+    vals: opt!(do_parse!(
+        arg0: ws!(match_named_parameter) >>
+        args: many0!(do_parse!(
+            tag!(",") >>
+            argn: ws!(match_named_parameter) >>
+            (argn)
+        )) >>
+        ({
+            let mut res = vec![arg0];
+            res.extend(args);
+            res
+        })
+    )) >>
+    tag!("}") >>
+    (MatchParameters::Named(vals.unwrap_or_default()))
 ));
 
 named!(expr_group<Input, Expr>, map!(
@@ -356,6 +392,16 @@ named!(match_parameter<Input, MatchParameter>, alt!(
     param_str_lit
 ));
 
+named!(match_named_parameter<Input, (&str, Option<MatchParameter>)>, do_parse!(
+    name: identifier >>
+    param: opt!(do_parse!(
+        ws!(tag!(":")) >>
+        param: match_parameter >>
+        (param)
+    )) >>
+    ((name, param))
+));
+
 named!(attr<Input, (&str, Option<Vec<Expr>>)>, do_parse!(
     tag!(".") >>
     attr: alt!(num_lit | identifier) >>
@@ -549,7 +595,7 @@ named_args!(match_else_block<'a>(s: &'a Syntax<'a>) <Input<'a>, When<'a>>, do_pa
     nws: opt!(tag!("-")) >>
     call!(tag_block_end, s) >>
     block: call!(parse_template, s) >>
-    (WS(pws.is_some(), nws.is_some()), None, vec![], block)
+    (WS(pws.is_some(), nws.is_some()), None, MatchParameters::Simple(vec![]), block)
 ));
 
 named_args!(when_block<'a>(s: &'a Syntax<'a>) <Input<'a>, When<'a>>, do_parse!(


Test patch:
diff --git a/testing/templates/match-custom-enum.html b/testing/templates/match-custom-enum.html
index cb45b8f..bec38b9 100644
--- a/testing/templates/match-custom-enum.html
+++ b/testing/templates/match-custom-enum.html
@@ -1,6 +1,6 @@
 {% match color %}
-{% when Color::Rgb with (r, g, b) %}
-Colorful: #{{ "{:02X}"|format(r) }}{{ "{:02X}"|format(g) }}{{ "{:02X}"|format(b) }}
+{% when Color::Rgb with {r, g: g, b: blue} %}
+Colorful: #{{ "{:02X}"|format(r) }}{{ "{:02X}"|format(g) }}{{ "{:02X}"|format(blue) }}
 {% when Color::GrayScale with (val) %}
 Gray: #{{ "{:02X}"|format(val) }}{{ "{:02X}"|format(val) }}{{ "{:02X}"|format(val) }}
 {% else %}
diff --git a/testing/tests/matches.rs b/testing/tests/matches.rs
index 89082ef..8657fa5 100644
--- a/testing/tests/matches.rs
+++ b/testing/tests/matches.rs
@@ -62,7 +62,7 @@ fn test_match_literal_num() {
 
 #[allow(dead_code)]
 enum Color {
-    Rgb(u32, u32, u32),
+    Rgb { r: u32, g: u32, b: u32 },
     GrayScale(u32),
     Cmyk(u32, u32, u32, u32),
 }
@@ -76,7 +76,11 @@ struct MatchCustomEnumTemplate {
 #[test]
 fn test_match_custom_enum() {
     let s = MatchCustomEnumTemplate {
-        color: Color::Rgb(160, 0, 255),
+        color: Color::Rgb {
+            r: 160,
+            g: 0,
+            b: 255,
+        },
     };
     assert_eq!(s.render().unwrap(), "\n\nColorful: #A000FF\n");
 }