fallthrough/
lib.rs

1//! This crate provides a [`fallthrough`] macro,
2//! which allows performing a pattern match with fallthrough through the arms,
3//! in the style of [C `switch`](https://en.cppreference.com/w/c/language/switch).
4#![forbid(unsafe_code)]
5#![warn(rust_2018_idioms, clippy::cargo, clippy::semicolon_if_nothing_returned)]
6
7/// Accepts a match scrutinee,
8/// followed by a comma-separated list of zero or more pattern match arms.
9/// All arms but the first must be preceded with a `'label: `. Only the first arm
10/// can access identifiers bound by the pattern match. Inside the match arms,
11/// calling `break 'label;` will jump to the label's corresponding match arm
12/// (you can only jump downwards). The list of arms can optionally be followed by a final
13/// trailing label, which can be used to jump out of the fallthrough entirely.
14///
15/// # Example
16///
17/// ```
18/// use fallthrough::fallthrough;
19///
20/// fn fall(scrutinee: u32) -> u32 {
21///     let mut ret: u32 = 0;
22///
23///     fallthrough!(scrutinee,
24///         val @ (0 | 63..) => ret = val + 7,
25///         'one: 1 => ret += 8,
26///         'two: 2 => ret += 9,
27///         'three: 3 if true => { ret += 10; break 'end },
28///         'four: 4 => ret = 42,
29///         'five: 5 => { ret += 1; break 'seven },
30///         'six: 6 => ret = 3,
31///         'seven: _ => ret *= 2,
32///         'end
33///     );
34///     ret
35/// }
36///
37/// fn main() {
38///     assert_eq!(fall(0), 34);
39///     assert_eq!(fall(1), 27);
40///     assert_eq!(fall(2), 19);
41///     assert_eq!(fall(3), 10);
42///     assert_eq!(fall(4), 86);
43///     assert_eq!(fall(5), 2);
44///     assert_eq!(fall(6), 6);
45///     assert_eq!(fall(7), 0);
46///     assert_eq!(fall(64), 98);
47/// }
48/// ```
49#[macro_export]
50macro_rules! fallthrough {
51    ($scrutinee:expr $(,)?) => {
52        match $scrutinee {}
53    };
54    ($scrutinee:expr, $first_pat:pat $(if $first_guard:expr)? => $first_body:expr $(, $label:lifetime $(: $pat:pat $(if $guard:expr)? => $body:expr)?)* $(,)?) => {
55        $crate::fallthrough_rec!{ (match $scrutinee {
56            $first_pat $(if $first_guard)? => $first_body,
57            $($($pat $(if $guard)? => break $label,)?)*
58        }), $($label $(: ($body))?),* }
59    };
60}
61
62#[macro_export]
63#[doc(hidden)]
64macro_rules! fallthrough_rec {
65    (($($acc:tt)*),) => {$($acc)*};
66    (($($acc:tt)*), $label:lifetime) => {
67        $label: {
68            $($acc)*
69        }
70    };
71    (($($acc:tt)*), $label:lifetime: ($($body:tt)*) $(,$($follow:tt)*)? ) => {
72
73        $crate::fallthrough_rec!{($label: {
74                $($acc)*
75            }
76            $($body)*
77        ), $($($follow)*)?}
78    };
79}