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}