edisp/
lib.rs

1//! Dispatch-on-collect for Rust enums.
2//!
3//! This crate allows to dispatch enums
4//! yielded from an iterator, depending on their variants, with no runtime
5//! costs.
6//!
7//! **Note:** This documentation describes what *should* be done, not the
8//! current state of the crate. Every feature documented here will be
9//! implemented prior first beta release.
10//!
11//! # Dispatching on `std` enums
12//!
13//! This crate provides dispatching for enums defined in `std`. Values can be
14//! collected in any type that implements both [`Default`] and [`Extend`] traits.
15//! This dispatching consists in a trait generated for each enum, which can be
16//! called on every `Iterator`, like so:
17//!
18//! ```
19//! use edisp::prelude::*;
20//!
21//! // Use your regular iterator
22//! let iter = vec![
23//!     Ok(42),
24//!     Ok(0),
25//!     Err("User not found"),
26//!     Err("System error"),
27//! ].into_iter();
28//!
29//! // Call the correct method, and that's all!
30//! let (some_successes, some_errors): (Vec<_>, Vec<_>) = iter.dispatch_result();
31//!
32//! assert_eq!(some_successes, vec![42, 0]);
33//! assert_eq!(some_errors, vec!["User not found", "System error"]);
34//! ```
35//!
36//! # Dispatching on other crate's enums
37//!
38//! Dispatching code is generated with either `derive` macro or with declarative
39//! macro. The first method allows to quickly generate boilerplate without
40//! needing to write the enum name and variants twice. The second allows to get
41//! rid of the procedural macro dependencies, `syn` and `quote`, and reduces
42//! compilation time.
43//!
44//! Values can then be collected in any type that implements both [`Default`]
45//! and [`Extend`] traits.
46//!
47//! ## Using `derive` macro
48//!
49//! **Note:** This feature is not currently avalaible. It will be implemented
50//! before first beta release.
51//!
52//! This crate provides a custom `derive` macro allowing which automatically
53//! implements traits required for dispatching, as shown in the following code
54//! snippet:
55//!
56//! ```rust
57//! use edisp::prelude::*;
58//!
59//! #[derive(Dispatch)]
60//! enum MyOwnEnum<T> {
61//!     Character(char),
62//!     Custom(T),
63//! }
64//!
65//! // Practical use-case:
66//! // First, create an iterator of `MyOwnEnum<&'static str>`
67//! let iter = vec![
68//!     MyOwnEnum::Character('λ'),
69//!     MyOwnEnum::Custom("horse"),
70//!     MyOwnEnum::Custom("manatee"),
71//!     MyOwnEnum::Character('!'),
72//! ].into_iter();
73//!
74//! // Then call it
75//! let (some_characters, some_strs): (Vec<_>, Vec<_>) = MyOwnEnum::dispatch(iter);
76//!
77//! // And it does what you expect!
78//! assert_eq!(
79//!     some_characters,
80//!     vec!['λ', '!'],
81//! );
82//!
83//! assert_eq!(
84//!     some_strs,
85//!     vec!["horse", "manatee"],
86//! );
87//! ```
88//!
89//! **Note:** This feature is not currently implemented, and as such can't be
90//! turned off.
91//!
92//! The custom derive feature can be disabled by disabling `derive` feature.
93//!
94//! ## Using declarative macro
95//!
96//! This crate provides a macro entitled `implement_dispatch`. It allows to
97//! generate traits required for dispatching. Everything wraps up like this:
98//!
99//! ```rust
100//! use edisp::prelude::*;
101//!
102//! enum MyOwnEnum<T> {
103//!     Character(char),
104//!     Custom(T),
105//! }
106//!
107//! // Implements the required trait (in this case, CollectDispatch2)
108//! implement_dispatch!(
109//!     MyOwnEnum<T>,
110//!     Character(char),
111//!     Custom(T),
112//! );
113//!
114//! // Practical use-case:
115//! // First, create an iterator of `MyOwnEnum<&'static str>`
116//! let iter = vec![
117//!     MyOwnEnum::Character('λ'),
118//!     MyOwnEnum::Custom("horse"),
119//!     MyOwnEnum::Custom("manatee"),
120//!     MyOwnEnum::Character('!'),
121//! ].into_iter();
122//!
123//! // Then call it
124//! let (some_characters, some_strs): (Vec<_>, Vec<_>) = MyOwnEnum::dispatch(iter);
125//!
126//! // And it does what you expect!
127//! assert_eq!(
128//!     some_characters,
129//!     vec!['λ', '!'],
130//! );
131//!
132//! assert_eq!(
133//!     some_strs,
134//!     vec!["horse", "manatee"],
135//! );
136//! ```
137//!
138//! [`Default`]: https://doc.rust-lang.org/std/default/trait.Default.html
139//! [`Extend`]: https://doc.rust-lang.org/std/iter/trait.Extend.html
140
141#![forbid(missing_docs)]
142
143pub mod dispatchers;
144pub mod prelude;
145pub mod std_enums;
146
147/// Implements a given dispatcher trait for a given enum.
148///
149/// This macro is meant to be used internally, and should **not** be called
150/// by the user. It does not bring any new feature, and won't be faster or
151/// whetever.
152#[macro_export]
153macro_rules! implement_dispatcher_trait {
154    (
155        $enum_name:ident ( $( $ty_arg:tt ),* $( , )? ),
156        $( (
157            $variant_name:ident,
158            $inner_type:ty,
159            $container_name:ident,
160            $container_letter:ident
161        ) ),+ $( , )?
162    ) => {
163        impl<
164            $( $ty_arg, )*
165            $( $container_letter, )+
166        > $crate::dispatchers::Dispatch<( $( $container_letter, )+ )> for $enum_name< $( $ty_arg, )* >
167        where
168        $(
169            $container_letter: Default + Extend<$inner_type>,
170        )+
171        {
172            fn dispatch<I>(iter: I) -> ( $( $container_letter, )+ )
173            where
174                I: Iterator<Item = $enum_name< $( $ty_arg, )* >>,
175            {
176                $(
177                    let mut $container_name = $container_letter::default();
178                )+
179
180                use $enum_name::*;
181                for element in iter {
182                    match element {
183                        $(
184                            $variant_name(value) => $container_name.extend(Some(value)),
185                        )+
186                    }
187                }
188
189                (
190                    $(
191                        $container_name,
192                    )+
193                )
194            }
195        }
196    }
197}
198
199/// Implements the dispatch for an enum.
200///
201/// ```
202/// use edisp::prelude::*;
203///
204/// enum MyResult<T, E> {
205///     MyOk(T),
206///     MyErr(E)
207/// }
208///
209/// implement_dispatch!(MyResult<T, E>, MyOk(T), MyErr(E));
210///
211/// enum MyEnum {
212///     Integer(u8),
213///     Other(char),
214/// }
215///
216/// implement_dispatch!(MyEnum, Integer(u8), Other(char));
217/// ```
218#[macro_export]
219macro_rules! implement_dispatch {
220    ($_:ident $( < $( $__:tt ),+ $( , )? > )? $( , )? ) => {
221        compile_error!("It is not necessary to implement `Dispatch` on an empty enum.");
222    };
223
224    ($_:ident $( < $( $__:tt),+ $( , )? > )?,
225     $___: ident ($____: ty) $( , )?
226    ) => {
227        compile_error!("It is not necessary to implement `Dispatch` on a single-variant enum. You can use `map` and then collect instead.");
228    };
229
230
231    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
232     $variant1_name: ident ($variant1_it: ty),
233     $variant2_name: ident ($variant2_it: ty) $( , )?
234    ) => {
235        implement_dispatcher_trait!(
236            $enum_name( $( $( $ty_arg, )+ )? ),
237            ($variant1_name, $variant1_it, container_a, A),
238            ($variant2_name, $variant2_it, container_b, B),
239        );
240    };
241
242    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
243     $variant1_name: ident ($variant1_it: ty),
244     $variant2_name: ident ($variant2_it: ty),
245     $variant3_name: ident ($variant3_it: ty) $( , )?
246    ) => {
247        implement_dispatcher_trait!(
248            $enum_name( $( $( $ty_arg, )+ )? ),
249            ($variant1_name, $variant1_it, container_1, A),
250            ($variant2_name, $variant2_it, container_2, B),
251            ($variant3_name, $variant3_it, container_3, C),
252        );
253    };
254
255    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
256     $variant1_name: ident ($variant1_it: ty),
257     $variant2_name: ident ($variant2_it: ty),
258     $variant3_name: ident ($variant3_it: ty),
259     $variant4_name: ident ($variant4_it: ty) $( , )?
260    ) => {
261        implement_dispatcher_trait!(
262            $enum_name( $( $( $ty_arg, )+ )? ),
263            ($variant1_name, $variant1_it, container_1, A),
264            ($variant2_name, $variant2_it, container_2, B),
265            ($variant3_name, $variant3_it, container_3, C),
266            ($variant4_name, $variant4_it, container_4, D),
267        );
268    };
269
270    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
271     $variant1_name: ident ($variant1_it: ty),
272     $variant2_name: ident ($variant2_it: ty),
273     $variant3_name: ident ($variant3_it: ty),
274     $variant4_name: ident ($variant4_it: ty),
275     $variant5_name: ident ($variant5_it: ty) $( , )?
276    ) => {
277        implement_dispatcher_trait!(
278            $enum_name( $( $( $ty_arg, )+ )? ),
279            ($variant1_name, $variant1_it, container_1, A),
280            ($variant2_name, $variant2_it, container_2, B),
281            ($variant3_name, $variant3_it, container_3, C),
282            ($variant4_name, $variant4_it, container_4, D),
283            ($variant5_name, $variant5_it, container_5, E),
284        );
285    };
286
287    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
288     $variant1_name: ident ($variant1_it: ty),
289     $variant2_name: ident ($variant2_it: ty),
290     $variant3_name: ident ($variant3_it: ty),
291     $variant4_name: ident ($variant4_it: ty),
292     $variant5_name: ident ($variant5_it: ty),
293     $variant6_name: ident ($variant6_it: ty) $( , )?
294    ) => {
295        implement_dispatcher_trait!(
296            $enum_name( $( $( $ty_arg, )+ )? ),
297            ($variant1_name, $variant1_it, container_1, A),
298            ($variant2_name, $variant2_it, container_2, B),
299            ($variant3_name, $variant3_it, container_3, C),
300            ($variant4_name, $variant4_it, container_4, D),
301            ($variant5_name, $variant5_it, container_5, E),
302            ($variant6_name, $variant6_it, container_6, F),
303        );
304    };
305
306    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
307     $variant1_name: ident ($variant1_it: ty),
308     $variant2_name: ident ($variant2_it: ty),
309     $variant3_name: ident ($variant3_it: ty),
310     $variant4_name: ident ($variant4_it: ty),
311     $variant5_name: ident ($variant5_it: ty),
312     $variant6_name: ident ($variant6_it: ty),
313     $variant7_name: ident ($variant7_it: ty) $( , )?
314    ) => {
315        implement_dispatcher_trait!(
316            $enum_name( $( $( $ty_arg, )+ )? ),
317            ($variant1_name, $variant1_it, container_1, A),
318            ($variant2_name, $variant2_it, container_2, B),
319            ($variant3_name, $variant3_it, container_3, C),
320            ($variant4_name, $variant4_it, container_4, D),
321            ($variant5_name, $variant5_it, container_5, E),
322            ($variant6_name, $variant6_it, container_6, F),
323            ($variant7_name, $variant7_it, container_7, G),
324        );
325    };
326
327    ($enum_name:ident $( < $( $ty_arg:tt ),+ $( , )? > )?,
328     $variant1_name: ident ($variant1_it: ty),
329     $variant2_name: ident ($variant2_it: ty),
330     $variant3_name: ident ($variant3_it: ty),
331     $variant4_name: ident ($variant4_it: ty),
332     $variant5_name: ident ($variant5_it: ty),
333     $variant6_name: ident ($variant6_it: ty),
334     $variant7_name: ident ($variant7_it: ty),
335     $variant8_name: ident ($variant8_it: ty) $( , )?
336    ) => {
337        implement_dispatcher_trait!(
338            $enum_name( $( $( $ty_arg, )+ )? ),
339            ($variant1_name, $variant1_it, container_1, A),
340            ($variant2_name, $variant2_it, container_2, B),
341            ($variant3_name, $variant3_it, container_3, C),
342            ($variant4_name, $variant4_it, container_4, D),
343            ($variant5_name, $variant5_it, container_5, E),
344            ($variant6_name, $variant6_it, container_6, F),
345            ($variant7_name, $variant7_it, container_7, G),
346            ($variant8_name, $variant8_it, container_8, H),
347        );
348    };
349}
350
351#[cfg(test)]
352mod tests {
353    use super::*;
354
355    /// Creates a dispatching test.
356    ///
357    /// This allows to generate tests for the `implement_dispatch` macro. These
358    /// tests run on *n*-variants enums, with concrete type and no lifetime
359    /// parameter. They are here to check that macro expansion are correct in
360    /// the simplest case.
361    ///
362    /// This macro is used internally to write tests. `edisp` users should not
363    /// use it in their code.
364    ///
365    /// The syntax of this macro proceeds as follow:
366    ///   - the name of the generated test,
367    ///   - a list of values, separated by comas, surrounded by square braces,
368    ///   designating the content of an iterator,
369    /// Then, for each variant used:
370    ///   - the name of the variant,
371    ///   - the type it contains, surrounded by parenthesis,
372    ///   - the name of its container (`c1`, `c2`, `c3`...),
373    ///   - the `Container` type which will be used to collect values (if
374    ///   you're unsure, simply use `Vec<_>`),
375    ///   - the expected content of the container.
376    macro_rules! implement_and_test_dispatching {
377        (
378            $test_name:ident,
379            // The values which will be yielded by the iterator
380            [ $( $input_value:expr ),* $( , )? ],
381            // Informations about each variant
382            $( (
383                // The name of the variant
384                $v_name:ident
385                // Its inner type
386                ($v_type:ty),
387                // The name of its container
388                $c_name:ident,
389                // The type of its container
390                $collect_type:ty,
391                // The expected content of the container
392                $c_content:tt $( , )?
393            ) ),* $( , )?
394        ) => {
395            #[test]
396            fn $test_name() {
397                use crate::prelude::*;
398
399                // Enum declaration
400                enum Enum {
401                    $( $v_name($v_type) ),*
402                }
403
404                // Allows caller not to specify the enum name for each variant
405                use Enum::*;
406
407                // Implements dispatch for the genrated enum
408                implement_dispatch!(
409                    Enum,
410                    $( $v_name($v_type) ),*
411                );
412
413                // Testing:
414                //   - Creation of the iterator
415                let iter = vec![ $( $input_value ),* ].into_iter();
416                //   - Dispatching
417                let ( $( $c_name ),* ): ( $( $collect_type ),* ) = Enum::dispatch(iter);
418                //   - Conformity check
419                $(
420                    assert_eq!($c_name, $c_content);
421                )*
422            }
423        };
424    }
425
426    // Generates a test for a two-variants enum.
427    implement_and_test_dispatching! {
428        dispatch_enum2,
429        [V1(42), V2("manatee")],
430        (V1(usize), c1, Vec<_>, [42]),
431        (V2(&'static str), c2, Vec<_>, ["manatee"]),
432    }
433
434    // Generates a test for a three-variants enum.
435    implement_and_test_dispatching! {
436        dispatch_enum3,
437        [V1(42), V2("manatee"), V3('!')],
438        (V1(usize), c1, Vec<_>, [42]),
439        (V2(&'static str), c2, Vec<_>, ["manatee"]),
440        (V3(char), c3, Vec<_>, ['!']),
441    }
442
443    // Generates a test for a four-variants enum.
444    implement_and_test_dispatching! {
445        dispatch_enum4,
446        [V1(42), V2("manatee"), V3('!'), V4(true)],
447        (V1(usize), c1, Vec<_>, [42]),
448        (V2(&'static str), c2, Vec<_>, ["manatee"]),
449        (V3(char), c3, Vec<_>, ['!']),
450        (V4(bool), c4, Vec<_>, [true]),
451    }
452
453    // Generates a test for a five-variants enum.
454    implement_and_test_dispatching! {
455        dispatch_enum5,
456        [V1(42), V2("manatee"), V3('!'), V4(true), V5(1.618)],
457        (V1(usize), c1, Vec<_>, [42]),
458        (V2(&'static str), c2, Vec<_>, ["manatee"]),
459        (V3(char), c3, Vec<_>, ['!']),
460        (V4(bool), c4, Vec<_>, [true]),
461        (V5(f64), c5, Vec<_>, [1.618]),
462    }
463
464    // Generates a test for a six-variants enum.
465    implement_and_test_dispatching! {
466        dispatch_enum6,
467        [V1(42), V2("manatee"), V3('!'), V4(true), V5(1.618), V6(-1)],
468        (V1(usize), c1, Vec<_>, [42]),
469        (V2(&'static str), c2, Vec<_>, ["manatee"]),
470        (V3(char), c3, Vec<_>, ['!']),
471        (V4(bool), c4, Vec<_>, [true]),
472        (V5(f64), c5, Vec<_>, [1.618]),
473        (V6(isize), c6, Vec<_>, [-1]),
474    }
475
476    // Generates a test for a seven-variants enum.
477    implement_and_test_dispatching! {
478        dispatch_enum7,
479        [V1(42), V2("manatee"), V3('!'), V4(true), V5(1.618), V6(-1), V7(101)],
480        (V1(usize), c1, Vec<_>, [42]),
481        (V2(&'static str), c2, Vec<_>, ["manatee"]),
482        (V3(char), c3, Vec<_>, ['!']),
483        (V4(bool), c4, Vec<_>, [true]),
484        (V5(f64), c5, Vec<_>, [1.618]),
485        (V6(isize), c6, Vec<_>, [-1]),
486        (V7(u8), c7, Vec<_>, [101]),
487    }
488
489    // Generates a test for a eight-variants enum.
490    implement_and_test_dispatching! {
491        dispatch_enum8,
492        [V1(42), V2("manatee"), V3('!'), V4(true), V5(1.618), V6(-1), V7(101), V8('§')],
493        (V1(usize), c1, Vec<_>, [42]),
494        (V2(&'static str), c2, Vec<_>, ["manatee"]),
495        (V3(char), c3, Vec<_>, ['!']),
496        (V4(bool), c4, Vec<_>, [true]),
497        (V5(f64), c5, Vec<_>, [1.618]),
498        (V6(isize), c6, Vec<_>, [-1]),
499        (V7(u8), c7, Vec<_>, [101]),
500        (V8(char), c8, Vec<_>, ['§']),
501    }
502}