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