typewit/macros/
polymatch.rs

1
2/// Match which expands top-level `|` patterns to multiple match arms.
3/// 
4/// [**examples below**](#examples)
5/// 
6/// ### Clarification
7/// 
8/// "top-level `|` patterns" means that the `|` is not inside some other pattern.
9/// <br>E.g.: the pattern in `Foo(x) | Bar(x) => ` is a top-level `|` pattern.
10/// <br>E.g.: the pattern in `(Foo(x) | Bar(x)) => ` is not a top-level `|` pattern, 
11/// because the `|` is inside parentheses.
12/// 
13/// # Syntax
14/// 
15/// This uses a `macro_rules!`-like syntax for the parameters of this macro
16/// 
17/// ```text
18/// $matched_expression:expr;
19///     $( $(|)? $($or_pattern:pat_param)|+ $(if $match_guard:expr)? => $arm_expr:expr ),*
20///     $(,)? 
21/// ```
22/// 
23/// [**example demonstrating all of this syntax**](#full-syntax)
24/// 
25/// # Examples
26/// 
27/// ### Basic
28/// 
29/// ```rust
30/// assert_eq!(debugify(Ok(3)), "3");
31/// assert_eq!(debugify(Err("hello")), r#""hello""#);
32/// 
33/// fn debugify(res: Result<u32, &'static str>) -> String {
34///     typewit::polymatch! {res; 
35///         Ok(x) | Err(x) => format!("{:?}", x)
36///     }
37/// }
38/// ```
39/// 
40/// The above invocation of `polymatch` expands to:
41/// 
42/// ```rust
43/// # assert_eq!(debugify(Ok(3)), "3");
44/// # assert_eq!(debugify(Err("hello")), r#""hello""#);
45/// # 
46/// # fn debugify(res: Result<u32, &'static str>) -> String {
47///     match res {
48///         Ok(x) => format!("{:?}", x),
49///         Err(x) => format!("{:?}", x),
50///     }
51/// # }
52/// ```
53/// 
54/// ### Full syntax
55/// 
56/// Example that uses all of the syntax supported by this macro.
57/// 
58/// ```rust
59/// assert_eq!(bar(Foo::Byte(3)), 6);
60/// assert_eq!(bar(Foo::Byte(9)), 18);
61/// assert_eq!(bar(Foo::Byte(10)), 10);
62/// 
63/// assert_eq!(bar(Foo::U16(3)), 6);
64/// assert_eq!(bar(Foo::U16(9)), 18);
65/// assert_eq!(bar(Foo::U16(10)), 10);
66/// 
67/// assert_eq!(bar(Foo::U32(3)), 0);
68/// 
69/// assert_eq!(bar(Foo::Long(3)), 0);
70/// 
71/// enum Foo {
72///     Byte(u8),
73///     U16(u16),
74///     U32(u32),
75///     Long(u64),
76/// }
77/// 
78/// const fn bar(foo: Foo) -> u64 {
79///     typewit::polymatch! {foo;
80///         // top-level  `|` patterns generate a match arm for every alternate pattern
81///         | Foo::Byte(x) 
82///         | Foo::U16(x) 
83///         if x < 10 
84///         => (x as u64) * 2,
85/// 
86///         Foo::Byte(x) | Foo::U16(x) => { x as u64 }
87/// 
88///         // `|` inside patterns behaves like in regular `match` expressions
89///         (Foo::U32(_) | Foo::Long(_)) => 0
90///     }
91/// }
92/// ```
93/// 
94/// The above invocation of `polymatch` expands to:
95/// ```rust
96/// # assert_eq!(bar(Foo::Byte(3)), 6);
97/// # assert_eq!(bar(Foo::Byte(9)), 18);
98/// # assert_eq!(bar(Foo::Byte(10)), 10);
99/// # 
100/// # assert_eq!(bar(Foo::U16(3)), 6);
101/// # assert_eq!(bar(Foo::U16(9)), 18);
102/// # assert_eq!(bar(Foo::U16(10)), 10);
103/// # 
104/// # assert_eq!(bar(Foo::U32(3)), 0);
105/// # 
106/// # assert_eq!(bar(Foo::Long(3)), 0);
107/// # enum Foo {
108/// #     Byte(u8),
109/// #     U16(u16),
110/// #     U32(u32),
111/// #     Long(u64),
112/// # }
113/// # 
114/// # const fn bar(foo: Foo) -> u64 {
115///     match foo {
116///         Foo::Byte(x) if x < 10  => (x as u64) * 2,
117///         Foo::U16(x) if x < 10  => (x as u64) * 2,
118/// 
119///         Foo::Byte(x) => { x as u64 }
120///         Foo::U16(x) => { x as u64 }
121/// 
122///         (Foo::U32(_) | Foo::Long(_)) => 0
123///     }
124/// # }
125/// ```
126/// 
127#[macro_export]
128macro_rules! polymatch {
129    ($matching:expr; $($match_arms:tt)*) => {
130        $crate::__polymatch!{($matching) () $($match_arms)*}
131    };
132    ($($tt:tt)*) => {
133        $crate::__::compile_error!{"expected arguments to be `matched expression; match arms`"}
134    };
135}
136
137
138
139#[doc(hidden)]
140#[macro_export]
141macro_rules! __polymatch {
142    // Parsing match like syntax
143    (
144        ($matching:expr)
145        ( $( ($($pattern:tt)*) => ($expr:expr) )* )
146
147        // Nothing left to parse
148        $(,)?
149    ) => {{
150        #[allow(unused_parens)]
151        match $matching {
152            $($($pattern)* => $expr,)*
153        }
154    }};
155    (
156        $fixed:tt
157        $prev_branch:tt
158
159        $(|)? $($pattern:pat_param)|+ $( if $guard:expr )? => $expr:expr
160        $(,$($rem:tt)*)?
161    ) => {{
162        $crate::__polymatch__handle_guard!{
163            $fixed
164            $prev_branch
165            (($($pattern)+) => $expr)
166            ($($guard)?)
167            ($($($rem)*)?)
168        }
169    }};
170    (
171        $fixed:tt
172        $prev_branch:tt
173
174        $(|)? $($pattern:pat_param)|+ $( if $guard:expr )? => $expr:block
175        $($rem:tt)*
176    ) => {{
177        $crate::__polymatch__handle_guard!{
178            $fixed
179            $prev_branch
180            (($($pattern)+) => $expr)
181            ($($guard)?)
182            ($($rem)*)
183        }
184    }};
185}
186
187#[doc(hidden)]
188#[macro_export]
189macro_rules! __polymatch__handle_guard {
190    (
191        $fixed:tt
192        ( $($prev_branch:tt)* )
193        (($($pattern:tt)+) => $expr:tt)
194        ()
195        ($($rem:tt)*)
196    ) => {
197        $crate::__polymatch!{
198            $fixed
199            (
200                $($prev_branch)*
201                $(($pattern) => ($expr))*
202            )
203            $($rem)*
204        }
205    };
206    (
207        $fixed:tt
208        ( $($prev_branch:tt)* )
209        (($($pattern:tt)+) => $expr:tt)
210        ($guard:expr)
211        ($($rem:tt)*)
212    ) => {
213        $crate::__polymatch!{
214            $fixed
215            (
216                $($prev_branch)*
217                $(($pattern if $guard) => ($expr))*
218            )
219            $($rem)*
220        }
221    };
222}
223