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}