enum_extract/lib.rs
1//! This crate provides 2 macros, `extract!` and `let_extract!`. See their
2//! individual macro-level documentation for more information.
3
4/// Extract the fields of a single variant from an enum, returning an
5/// `Option<T>` where `T` is either the single field, or a tuple of each of the
6/// fields in the order they are written.
7///
8/// ## Examples
9///
10/// Given the following enum:
11///
12/// ```ignore
13/// enum Foo {
14/// A(i32),
15/// B(i32, i32),
16/// C { x: i32, y: i32 },
17/// D { z: i32 },
18/// }
19/// ```
20///
21/// If the variant matches, it produces a `Some` of the fields in the matched
22/// variant.
23///
24/// ```rust
25/// # #[macro_use] extern crate enum_extract;
26/// # enum Foo {
27/// # A(i32),
28/// # B(i32, i32),
29/// # C { x: i32, y: i32 },
30/// # D { z: i32 },
31/// # }
32/// # fn main() {
33/// let a = Foo::A(10);
34/// assert_eq!(extract!(Foo::A(_), a), Some(10));
35///
36/// let d = Foo::D{ z: 20 };
37/// assert_eq!(extract!(Foo::D{z}, d), Some(20));
38/// # }
39/// ```
40///
41/// If there is more than one field in the enum variant, it produces a `Some` of a tuple
42///
43/// ```rust
44/// # #[macro_use] extern crate enum_extract;
45/// # enum Foo {
46/// # A(i32),
47/// # B(i32, i32),
48/// # C { x: i32, y: i32 },
49/// # D { z: i32 },
50/// # }
51/// # fn main() {
52/// let b = Foo::B(10, 20);
53/// assert_eq!(extract!(Foo::B(_, _), b), Some((10, 20)));
54///
55/// let c = Foo::C{ x: 30, y: 40 };
56/// assert_eq!(extract!(Foo::C{x, y}, c), Some((30, 40)));
57///
58/// // You can also control the order of the fields in the struct variant case!
59/// assert_eq!(extract!(Foo::C{y, x}, c), Some((40, 30)));
60/// # }
61/// ```
62///
63/// If the pattern doesn't match, it produces a `None`
64///
65/// ```rust
66/// # #[macro_use] extern crate enum_extract;
67/// # enum Foo {
68/// # A(i32),
69/// # B(i32, i32),
70/// # C { x: i32, y: i32 },
71/// # D { z: i32 },
72/// # }
73/// # fn main() {
74/// let b = Foo::B(10, 20);
75/// assert_eq!(extract!(Foo::A(_), b), None);
76/// # }
77/// ```
78#[macro_export]
79macro_rules! extract {
80 // Internal Variants
81 (@ANON_TUPLE [$(,)*], [$($ids:ident)*], $($p:ident)::+, $t:expr) => {
82 match $t {
83 $($p)::+ ( $($ids),* ) => Some(( $($ids),* )),
84 _ => None,
85 }
86 };
87 (@ANON_TUPLE [_], [$($ids:ident)*], $($p:ident)::+, $t:expr) => {
88 extract!(@ANON_TUPLE
89 [],
90 [$($ids)* x],
91 $($p)::+,
92 $t)
93 };
94 (@ANON_TUPLE [_, $($more:tt)*], [$($ids:ident)*], $($p:ident)::+, $t:expr) => {
95 extract!(@ANON_TUPLE
96 [$($more)*],
97 [$($ids)* x],
98 $($p)::+,
99 $t)
100 };
101
102 // Struct Variants
103 ($($p:ident)::+ { $($i:ident),* $(,)* } , $t:expr) => {
104 match $t {
105 $($p)::+ {$($i),*} => Some(($($i),*)),
106 _ => None
107 }
108 };
109
110 // Tuple Variants
111 ($($p:ident)::+ ( $($its:tt)* ) , $t:expr) => {
112 extract!(@ANON_TUPLE
113 [$($its)*],
114 [],
115 $($p)::+,
116 $t)
117 };
118}
119
120/// Extract the fields of a single variant from an enum, binding them into the
121/// current scope.
122///
123/// If the binding fails due to the variant being incorrect, execute the error
124/// expression. Alternatively, a partial `match` block may be written to handle
125/// the remaining cases.
126///
127/// ## Examples
128///
129/// Given the following enum:
130///
131/// ```ignore
132/// enum Foo {
133/// A(i32),
134/// B(i32, i32),
135/// C { x: i32, y: i32 },
136/// D { z: i32 },
137/// }
138/// ```
139///
140/// If the extract succeeds, the variable names written bind into the current
141/// scope.
142///
143/// ```rust
144/// # #[macro_use] extern crate enum_extract;
145/// # enum Foo {
146/// # A(i32),
147/// # B(i32, i32),
148/// # C { x: i32, y: i32 },
149/// # D { z: i32 },
150/// # }
151/// # fn main() {
152/// let a = Foo::A(10);
153/// let_extract!(Foo::A(x), a, panic!());
154/// assert_eq!(x, 10);
155/// # }
156/// ```
157///
158/// If it fails, the expression passed in as the 3rd argument is executed
159/// instead, and can cause execution to diverge.
160///
161/// ```rust
162/// # #[macro_use] extern crate enum_extract;
163/// # enum Foo {
164/// # A(i32),
165/// # B(i32, i32),
166/// # C { x: i32, y: i32 },
167/// # D { z: i32 },
168/// # }
169/// # fn main() {
170/// let a = Foo::A(10);
171/// let_extract!(Foo::B(x, y), a, return);
172/// unreachable!();
173/// # }
174/// ```
175///
176/// Alternatively, it can provide default values for the variable bindings, from
177/// left to right.
178///
179/// ```rust
180/// # #[macro_use] extern crate enum_extract;
181/// # enum Foo {
182/// # A(i32),
183/// # B(i32, i32),
184/// # C { x: i32, y: i32 },
185/// # D { z: i32 },
186/// # }
187/// # fn main() {
188/// let a = Foo::A(10);
189/// let_extract!(Foo::B(x, y), a, (40, 50));
190/// assert_eq!(x, 40);
191/// assert_eq!(y, 50);
192/// # }
193/// ```
194///
195/// The other cases can each be handled specifically with a match-like block.
196///
197/// ```rust
198/// # #[macro_use] extern crate enum_extract;
199/// # enum Foo {
200/// # A(i32),
201/// # B(i32, i32),
202/// # C { x: i32, y: i32 },
203/// # D { z: i32 },
204/// # }
205/// # fn main() {
206/// let a = Foo::A(10);
207/// let_extract!(Foo::B(x, y), a, match {
208/// Foo::A(x) => return,
209/// Foo::C{..} => unreachable!(),
210/// Foo::D{..} => unreachable!(),
211/// });
212/// unreachable!();
213/// # }
214/// ```
215///
216/// Struct variants are also supported, and can be written either as `{ field }`
217/// or `{ field: binding }`.
218///
219/// ```rust
220/// # #[macro_use] extern crate enum_extract;
221/// # enum Foo {
222/// # A(i32),
223/// # B(i32, i32),
224/// # C { x: i32, y: i32 },
225/// # D { z: i32 },
226/// # }
227/// # fn main() {
228/// let d = Foo::D { z: 10 };
229/// let_extract!(Foo::D{ z }, d, unreachable!());
230/// assert_eq!(z, 10);
231///
232/// let_extract!(Foo::D{ z: apples }, d, unreachable!());
233/// assert_eq!(apples, 10);
234/// # }
235/// ```
236#[macro_export]
237macro_rules! let_extract {
238 ($($p:ident)::+ ( $($i:ident),* $(,)* ) , $t:expr, match { $($body:tt)* }) => {
239 let ($($i,)*) = match $t {
240 $($p)::+ ($($i),*) => ($($i,)*),
241 $($body)*
242 };
243 };
244 ($($p:ident)::+ { $($i:ident),* $(,)* } , $t:expr, match { $($body:tt)* }) => {
245 let ($($i,)*) = match $t {
246 $($p)::+ {$($i),*} => ($($i,)*),
247 $($body)*
248 };
249 };
250 ($($p:ident)::+ { $($k:ident : $v:ident),* $(,)* } , $t:expr, match { $($body:tt)* }) => {
251 let ($($v,)*) = match $t {
252 $($p)::+ {$($k),*} => ($($k,)*),
253 $($body)*
254 };
255 };
256
257 ($($p:ident)::+ ( $($its:tt)* ) , $t:expr, $els:expr) => {
258 let_extract!($($p)::+ ( $($its)* ) , $t, match { _ => $els })
259 };
260 ($($p:ident)::+ { $($its:tt)* } , $t:expr, $els:expr) => {
261 let_extract!($($p)::+ { $($its)* } , $t, match { _ => $els })
262 };
263
264}
265
266#[cfg(test)]
267mod test {
268 enum Foo {
269 A(u32, u32),
270 B(u32)
271 }
272
273 enum Bar {
274 C { x: u32, y: u32 },
275 D { z: u32 },
276 }
277
278 #[test]
279 fn test() {
280 let f: Foo = Foo::A(10, 20);
281
282 let x = extract!(Foo::A(_, _), f);
283 assert_eq!(x, Some((10, 20)));
284
285 let x = extract!(Foo::A(_, _,), f);
286 assert_eq!(x, Some((10, 20)));
287
288 let_extract!(Foo::A(x, y), f, panic!());
289 assert_eq!(x, 10);
290 assert_eq!(y, 20);
291
292 let_extract!(Foo::A(x, y,), f, panic!());
293 assert_eq!(x, 10);
294 assert_eq!(y, 20);
295
296 let f: Foo = Foo::B(8);
297 let x = extract!(Foo::B(_), f);
298 assert_eq!(x, Some(8));
299
300 let_extract!(Foo::B(z), f, panic!());
301 assert_eq!(z, 8);
302
303 let f: Bar = Bar::C{ x: 10, y: 20 };
304
305 let x = extract!(Bar::C{x, y}, f);
306 assert_eq!(x, Some((10, 20)));
307
308 let x = extract!(Bar::C{x, y,}, f);
309 assert_eq!(x, Some((10, 20)));
310
311 let_extract!(Bar::C{x, y}, f, panic!());
312 assert_eq!(x, 10);
313 assert_eq!(y, 20);
314
315 let_extract!(
316 Bar::C{
317 x: a,
318 y: b,
319 },
320 f,
321 panic!()
322 );
323 assert_eq!(a, 10);
324 assert_eq!(b, 20);
325
326 let f: Bar = Bar::D{ z: 8 };
327 let x = extract!(Bar::D{z}, f);
328 assert_eq!(x, Some(8));
329
330 let_extract!(Bar::D{z}, f, panic!());
331 assert_eq!(z, 8);
332
333 let_extract!(Bar::D{z: x}, f, panic!());
334 assert_eq!(x, 8);
335
336 // let f: Bar = Bar::C{ x: 10, y: 20 };
337 let_extract!(Bar::D{z}, f, match {
338 Bar::C{x, y} => panic!("Saw {:?} {:?}", x, y)
339 });
340 assert_eq!(z, 8);
341 }
342
343 #[test]
344 fn alternate_result() {
345 let f: Foo = Foo::A(10, 20);
346
347 let_extract!(Foo::B(x), f, match { Foo::A(x, _) => (x,) });
348 assert_eq!(x, 10);
349
350 let_extract!(Some(y), None, (10,));
351 assert_eq!(y, 10);
352 }
353}