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}