macro_utils/
if_match.rs

1// MIT License
2//
3// Copyright (c) 2018 Guillaume Gomez
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23/// A macro to make big else if conditions easier to read:
24///
25/// ```
26/// # #[macro_use] extern crate macro_utils;
27/// let s = "bateau";
28///
29/// if_match! {
30///     s == "voiture" => println!("It rolls!"),
31///     s == "avion"   => println!("It flies!"),
32///     s == "pieds"   => println!("It walks!"),
33///     s == "fusée"   => println!("It goes through space!"),
34///     s == "bateau"  => println!("It moves on water!"),
35///     else           => println!("I dont't know how it moves...")
36/// }
37/// ```
38///
39/// You can use it just like you would use conditions:
40///
41/// ```
42/// # #[macro_use] extern crate macro_utils;
43/// let x = -1;
44///
45/// let result = if_match! {
46///     x >= 0 => "it's a positive number",
47///     else   => "it's a negative number",
48/// };
49///
50/// assert_eq!(result, "it's a negative number");
51/// ```
52///
53/// And of course, the `else` condition is completely optional:
54///
55/// ```
56/// # #[macro_use] extern crate macro_utils;
57/// let x = 12;
58///
59/// if_match! {
60///     x & 1 == 0 => println!("it is even"),
61///     x & 1 == 1 => println!("it is odd"),
62/// }
63/// ```
64///
65/// Want to use `if let` conditions too? Here you go:
66///
67/// ```
68/// # #[macro_use] extern crate macro_utils;
69/// let v = 12;
70/// let y = if_match! {
71///     let 0 = 1 => 0,
72///     v < 1 => 1,
73///     v > 10 => 10,
74///     let 0 = 1 => 0,
75///     else => v
76/// };
77/// assert_eq!(y, 10);
78/// ```
79#[macro_export]
80macro_rules! if_match {
81    ($(let $expr:pat =)* $cond:expr => $then:expr $(,)*) => {
82        if $(let $expr =)* $cond {
83            $then
84        }
85    };
86    ($(let $expr:pat =)* $cond:expr => $then:expr, else => $elsethen:expr $(,)*) => {
87        if $(let $expr =)* $cond {
88            $then
89        } else {
90            $elsethen
91        }
92    };
93    ($(let $expr:pat =)* $cond:expr => $then:expr, $($(let $expr2:pat =)* $else_cond:expr => $else_then:expr,)* else => $else_expr:expr $(,)*) => {
94        if $(let $expr =)* $cond {
95            $then
96        } $(else if $(let $expr2 =)* $else_cond {
97            $else_then
98        })* else {
99            $else_expr
100        }
101    };
102    ($(let $expr:pat =)* $cond:expr => $then:expr, $($(let $expr2:pat =)* $more:expr => $more_then:expr $(,)* )*) => {
103        if $(let $expr =)* $cond {
104            $then
105        } $(else if $(let $expr2 =)* $more {
106            $more_then
107        })*
108    };
109    () => {};
110}
111
112#[test]
113fn if_match() {
114    fn check_complete(v: i32) -> i32 {
115        if_match! {
116            v < 1 => 1,
117            v > 10 => 10,
118            else => v,
119        }
120    }
121
122    let x = 12;
123    let is_even = if_match! {
124        x % 2 == 0 => true,
125        else => false,
126    };
127    assert_eq!(is_even, true);
128
129    let mut not_zero = false;
130    if_match! {
131        x != 0 => {
132            not_zero = true;
133        },
134    }
135    assert_eq!(not_zero, true);
136
137    assert_eq!(check_complete(1), 1);
138    assert_eq!(check_complete(0), 1);
139    assert_eq!(check_complete(12), 10);
140}
141
142#[test]
143fn let_match() {
144    let v = 12;
145    let y = if_match! {
146        let 0 = 1 => 0,
147        v < 1 => 1,
148        v > 10 => 10,
149        let 0 = 1 => 0,
150        else => v
151    };
152    assert_eq!(y, 10);
153
154    let y = if_match! {
155        v < 1 => 1,
156        v > 10 => 10,
157        let 0 = 1 => 0,
158        else => v,
159    };
160    assert_eq!(y, 10);
161
162    let mut z = 0;
163    if_match! {
164        v < 1 => z = 1,
165        v > 10 => z = 10,
166        let 0 = 1 => z = 0,
167    }
168    assert_eq!(z, 10);
169
170    if_match! {
171        v < 1 => z = 10,
172        v > 10 => z = 2,
173        let 0 = 1 => z = 0
174    }
175    assert_eq!(z, 2);
176
177    if_match! {
178        let 0 = 1 => println!("1"),
179    }
180}
181
182#[test]
183fn if_match_optional_end_comma() {
184    let x = 42;
185
186    if_match! {
187        x != 0 => println!("ok")
188    }
189
190    if_match! {
191        x != 0 => println!("ok"),
192        else => print!("0")
193    }
194
195    if_match! {
196        x & 1 == 0 => println!("it is even"),
197        x & 1 == 1 => println!("it is odd"),
198        else => print!("wtf is that")
199    }
200
201    if_match! {
202        x & 1 == 0 => println!("it is even"),
203        x & 1 == 1 => println!("it is odd")
204    }
205}