1 //! This crate provides a single macro called `if_chain!`.
2 //!
3 //! `if_chain!` lets you write long chains of nested `if` and `if let`
4 //! statements without the associated rightward drift. It also supports multiple
5 //! patterns (e.g. `if let Foo(a) | Bar(a) = b`) in places where Rust would
6 //! normally not allow them.
7 //!
8 //! See the associated [blog post] for the background behind this crate.
9 //!
10 //! [blog post]: https://lambda.xyz/blog/if-chain
11 //!
12 //! # Note about recursion limits
13 //!
14 //! If you run into "recursion limit reached" errors while using this macro, try
15 //! adding
16 //!
17 //! ```rust,ignore
18 //! #![recursion_limit = "1000"]
19 //! ```
20 //!
21 //! to the top of your crate.
22 //!
23 //! # Examples
24 //!
25 //! ## Quick start
26 //!
27 //! ```rust,ignore
28 //! if_chain! {
29 //!     if let Some(y) = x;
30 //!     if y.len() == 2;
31 //!     if let Some(z) = y;
32 //!     then {
33 //!         do_stuff_with(z);
34 //!     }
35 //! }
36 //! ```
37 //!
38 //! becomes
39 //!
40 //! ```rust,ignore
41 //! if let Some(y) = x {
42 //!     if y.len() == 2 {
43 //!         if let Some(z) = y {
44 //!             do_stuff_with(z);
45 //!         }
46 //!     }
47 //! }
48 //! ```
49 //!
50 //! ## Fallback values with `else`
51 //!
52 //! ```rust,ignore
53 //! if_chain! {
54 //!     if let Some(y) = x;
55 //!     if let Some(z) = y;
56 //!     then {
57 //!         do_stuff_with(z)
58 //!     } else {
59 //!         do_something_else()
60 //!     }
61 //! }
62 //! ```
63 //!
64 //! becomes
65 //!
66 //! ```rust,ignore
67 //! if let Some(y) = x {
68 //!     if let Some(z) = y {
69 //!         do_stuff_with(z)
70 //!     } else {
71 //!         do_something_else()
72 //!     }
73 //! } else {
74 //!     do_something_else()
75 //! }
76 //! ```
77 //!
78 //! ## Intermediate variables with `let`
79 //!
80 //! ```rust,ignore
81 //! if_chain! {
82 //!     if let Some(y) = x;
83 //!     let z = y.some().complicated().expression();
84 //!     if z == 42;
85 //!     then {
86 //!        do_stuff_with(y);
87 //!     }
88 //! }
89 //! ```
90 //!
91 //! becomes
92 //!
93 //! ```rust,ignore
94 //! if let Some(y) = x {
95 //!     let z = y.some().complicated().expression();
96 //!     if z == 42 {
97 //!         do_stuff_with(y);
98 //!     }
99 //! }
100 //! ```
101 //!
102 //! ## Type ascription
103 //!
104 //! ```rust,ignore
105 //! let mut x = some_generic_computation();
106 //! if_chain! {
107 //!     if x > 7;
108 //!     let y: u32 = another_generic_computation();
109 //!     then { x += y }
110 //!     else { x += 1 }
111 //! }
112 //! ```
113 //!
114 //! becomes
115 //!
116 //! ```rust,ignore
117 //! let mut x = some_generic_computation();
118 //! if x > 7 {
119 //!     let y: u32 = another_generic_computation();
120 //!     x += y
121 //! } else {
122 //!     x += 1
123 //! }
124 //! ```
125 //!
126 //! ## Multiple patterns
127 //!
128 //! ```rust,ignore
129 //! if_chain! {
130 //!     if let Foo(y) | Bar(y) | Baz(y) = x;
131 //!     let Bubbles(z) | Buttercup(z) | Blossom(z) = y;
132 //!     then { do_stuff_with(z) }
133 //! }
134 //! ```
135 //!
136 //! becomes
137 //!
138 //! ```rust,ignore
139 //! match x {
140 //!     Foo(y) | Bar(y) | Baz(y) => match y {
141 //!         Bubbles(z) | Buttercup(z) | Blossom(z) => do_stuff_with(z)
142 //!     },
143 //!     _ => {}
144 //! }
145 //! ```
146 //!
147 //! Note that if you use a plain `let`, then `if_chain!` assumes that the
148 //! pattern is *irrefutable* (always matches) and doesn't add a fallback branch.
149 
150 #![cfg_attr(not(test), no_std)]
151 
152 /// Macro for writing nested `if let` expressions.
153 ///
154 /// See the crate documentation for information on how to use this macro.
155 #[macro_export(local_inner_macros)]
156 macro_rules! if_chain {
157     ($($tt:tt)*) => {
158         __if_chain! { @init () $($tt)* }
159     };
160 }
161 
162 #[doc(hidden)]
163 #[macro_export(local_inner_macros)]
164 macro_rules! __if_chain {
165     // Expand with both a successful case and a fallback
166     (@init ($($tt:tt)*) then { $($then:tt)* } else { $($other:tt)* }) => {
167         __if_chain! { @expand { $($other)* } $($tt)* then { $($then)* } }
168     };
169     // Expand with no fallback
170     (@init ($($tt:tt)*) then { $($then:tt)* }) => {
171         __if_chain! { @expand {} $($tt)* then { $($then)* } }
172     };
173     // Munch everything until either of the arms above can be matched.
174     // Munched tokens are placed into `$($tt)*`
175     (@init ($($tt:tt)*) $head:tt $($tail:tt)*) => {
176         __if_chain! { @init ($($tt)* $head) $($tail)* }
177     };
178 
179     // `let` with a single pattern
180     (@expand { $($other:tt)* } let $pat:pat = $expr:expr; $($tt:tt)+) => {
181         {
182             let $pat = $expr;
183             __if_chain! { @expand { $($other)* } $($tt)+ }
184         }
185     };
186     // `let` with a single identifier and a type hint
187     (@expand { $($other:tt)* } let $ident:ident: $ty:ty = $expr:expr; $($tt:tt)+) => {
188         {
189             let $ident: $ty = $expr;
190             __if_chain! { @expand { $($other)* } $($tt)+ }
191         }
192     };
193     // `let` with multiple patterns
194     (@expand { $($other:tt)* } let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => {
195         match $expr {
196             $pat1 | $($pat)|+ => { __if_chain! { @expand { $($other)* } $($tt)+ } }
197         }
198     };
199     // `if let` with a single pattern
200     (@expand {} if let $pat:pat = $expr:expr; $($tt:tt)+) => {
201         if let $pat = $expr {
202             __if_chain! { @expand {} $($tt)+ }
203         }
204     };
205     // `if let` with a single pattern and a fallback
206     (@expand { $($other:tt)+ } if let $pat:pat = $expr:expr; $($tt:tt)+) => {
207         if let $pat = $expr {
208             __if_chain! { @expand { $($other)+ } $($tt)+ }
209         } else {
210             $($other)+
211         }
212     };
213     // `if let` with multiple matterns and a fallback (if present)
214     (@expand { $($other:tt)* } if let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => {
215         match $expr {
216             $pat1 | $($pat)|+ => { __if_chain! { @expand { $($other)* } $($tt)+ } },
217             _ => { $($other)* }
218         }
219     };
220     // `if` with a successful case
221     (@expand {} if $expr:expr; $($tt:tt)+) => {
222         if $expr {
223             __if_chain! { @expand {} $($tt)+ }
224         }
225     };
226     // `if` with both a successful case and a fallback
227     (@expand { $($other:tt)+ } if $expr:expr; $($tt:tt)+) => {
228         if $expr {
229             __if_chain! { @expand { $($other)+ } $($tt)+ }
230         } else {
231             $($other)+
232         }
233     };
234     // Final macro call
235     (@expand { $($other:tt)* } then { $($then:tt)* }) => {
236         $($then)*
237     };
238 }
239 
240 #[cfg(test)]
241 mod tests {
242     #[test]
simple()243     fn simple() {
244         let x: Option<Result<Option<String>, (u32, u32)>> = Some(Err((41, 42)));
245         let mut success = false;
246         if_chain! {
247             if let Some(y) = x;
248             if let Err(z) = y;
249             let (_, b) = z;
250             if b == 42;
251             then { success = true; }
252         }
253         assert!(success);
254     }
255 
256     #[test]
empty()257     fn empty() {
258         let success;
259         if_chain! {
260             then { success = true; }
261         }
262         assert!(success);
263     }
264 
265     #[test]
empty_with_else()266     fn empty_with_else() {
267         let success;
268         if_chain! {
269             then { success = true; }
270             else { unreachable!(); }
271         }
272         assert!(success);
273     }
274 
275     #[test]
if_let_multiple_patterns()276     fn if_let_multiple_patterns() {
277         #[derive(Copy, Clone)]
278         enum Robot { Nano, Biscuit1, Biscuit2 }
279         for &(robot, expected) in &[
280             (Robot::Nano, false),
281             (Robot::Biscuit1, true),
282             (Robot::Biscuit2, true),
283         ] {
284             let is_biscuit = if_chain! {
285                 if let Robot::Biscuit1 | Robot::Biscuit2 = robot;
286                 then { true } else { false }
287             };
288             assert_eq!(is_biscuit, expected);
289         }
290     }
291 
292     #[test]
let_multiple_patterns()293     fn let_multiple_patterns() {
294         let x: Result<u32, u32> = Ok(42);
295         if_chain! {
296             let Ok(x) | Err(x) = x;
297             then { assert_eq!(x, 42); }
298             else { panic!(); }
299         }
300     }
301 
302     #[test]
let_type_annotation_patterns()303     fn let_type_annotation_patterns() {
304         let mut x = 1;
305         if_chain! {
306             if x > 0;
307             let y: u32 = 2;
308 
309             then { x += y; }
310             else { x += 1; }
311         }
312         assert_eq!(x, 3);
313     }
314 }
315