Skip to main content

iter_enumeration/
lib.rs

1#![no_std]
2
3//! This crate provides utility to unify [`Iterator`]s over the same type.
4//!
5//! Dealing with [`Iterator`] types originating from different parts of the code,
6//! such as separate branches of a `match`, can be a bit cumbersome.
7//! A similar problem arises with an iterator over iterators.
8//!
9//! Alternative solutions to this problem:
10//! One can [`Iterator::collect`] the iterator, do something with `Box<dyn Iterator>`,
11//! or redesign altogether.
12//!
13//! However, this crate allows you to do this:
14//!
15//! # Example
16//!
17//! ```rust
18//! use iter_enumeration::{IterEnum2, IntoIterEnum2};
19//!
20//! // start with any iterator
21//! let it = 0..10;
22//!
23//! // have some branching code
24//! let it = if true {
25//!     // and call the trait extension method
26//!     it.map(|i| i * i).iter_enum_2a()
27//! } else {
28//!     // or use the enum directly
29//!     IterEnum2::B(it.filter(|i| i % 2 == 0))
30//! };
31//!
32//! // continue with the unified iterator enum type
33//! let it = it.inspect(|i| println!("{i}"));
34//!
35//! // or have more branches, up to 6, currently
36//! use iter_enumeration::IntoIterEnum3;
37//! let it = match 42 {
38//!     0 => it.iter_enum_3a(),
39//!     1 => it.take(10).iter_enum_3b(),
40//!     _ => it.skip(10).iter_enum_3c(),
41//! };
42//! ```
43//!
44//! Note that there is also [iter-enum](https://crates.io/crates/iter-enum),
45//! which provides a similar solution but differs from what I want:
46//! You either have to define the enum yourself or use [auto_enum](https://crates.io/crates/auto_enums),
47//! which uses proc-macros.
48
49macro_rules! impl_iter_enum {
50    (
51        $EnumId:ident,
52        $IntoTraitId:ident,
53        $((
54            $A:ident,
55            $iter_enum_a:ident,
56            ($($Before:ident),*),($($After:ident),*)
57        )),*,
58    )  => {
59        #[derive(Clone, Debug)]
60        pub enum $EnumId<$($A),*> {
61            $($A($A),)*
62        }
63
64        impl<I, $($A: Iterator<Item = I>),*> Iterator for $EnumId<$($A),*> {
65            type Item = I;
66            fn next(&mut self) -> Option<Self::Item> {
67                match self {
68                    $(Self::$A(inner) => inner.next()),*
69                }
70            }
71        }
72
73        pub trait $IntoTraitId: Sized {
74            $(
75                fn $iter_enum_a<$($Before,)*$($After,)*>(self) -> $EnumId<$($Before,)* Self, $($After,)*> {
76                    $EnumId::$A(self)
77                }
78            )*
79        }
80
81        impl<T: Iterator> $IntoTraitId for T {}
82    };
83}
84
85impl_iter_enum!(
86    IterEnum2,
87    IntoIterEnum2,
88    (A, iter_enum_2a, (), (B)),
89    (B, iter_enum_2b, (A), ()),
90);
91
92impl_iter_enum!(
93    IterEnum3,
94    IntoIterEnum3,
95    (A, iter_enum_3a, (), (B, C)),
96    (B, iter_enum_3b, (A), (C)),
97    (C, iter_enum_3c, (A, B), ()),
98);
99
100impl_iter_enum!(
101    IterEnum4,
102    IntoIterEnum4,
103    (A, iter_enum_4a, (), (B, C, D)),
104    (B, iter_enum_4b, (A), (C, D)),
105    (C, iter_enum_4c, (A, B), (D)),
106    (D, iter_enum_4d, (A, B, C), ()),
107);
108
109impl_iter_enum!(
110    IterEnum5,
111    IntoIterEnum5,
112    (A, iter_enum_5a, (), (B, C, D, E)),
113    (B, iter_enum_5b, (A), (C, D, E)),
114    (C, iter_enum_5c, (A, B), (D, E)),
115    (D, iter_enum_5d, (A, B, C), (E)),
116    (E, iter_enum_5e, (A, B, C, D), ()),
117);
118
119impl_iter_enum!(
120    IterEnum6,
121    IntoIterEnum6,
122    (A, iter_enum_6a, (), (B, C, D, E, F)),
123    (B, iter_enum_6b, (A), (C, D, E, F)),
124    (C, iter_enum_6c, (A, B), (D, E, F)),
125    (D, iter_enum_6d, (A, B, C), (E, F)),
126    (E, iter_enum_6e, (A, B, C, D), (F)),
127    (F, iter_enum_6f, (A, B, C, D, F), ()),
128);
129
130#[cfg(test)]
131mod tests {
132    use core::iter::{empty, once};
133
134    use super::*;
135
136    #[test]
137    fn if_branch() {
138        let eval = |b| {
139            if b {
140                (0..10).iter_enum_2a()
141            } else {
142                (0..10).filter(|i| i % 2 == 0).iter_enum_2b()
143            }
144            .count()
145        };
146
147        assert!(eval(true) == 10);
148        assert!(eval(false) == 5);
149    }
150
151    #[test]
152    fn match_branch() {
153        let eval = |i| {
154            match i {
155                0 => (0..1).iter_enum_3a(),
156                1 => (0..3).iter_enum_3b(),
157                _ => (0..4).iter_enum_3c(),
158            }
159            .count()
160        };
161
162        assert!(eval(0) == 1);
163        assert!(eval(1) == 3);
164        assert!(eval(42) == 4);
165    }
166
167    #[test]
168    fn iterator_of_iterators() {
169        assert!(
170            empty()
171                .chain(once((0..1).iter_enum_3a()))
172                .chain(once((0..2).map(|i| i - 1).iter_enum_3b()))
173                .chain(once((0..3).filter(|i| i % 2 == 0).iter_enum_3c()))
174                .fold(0, |sum, it| sum + it.sum::<i32>())
175                == 1
176        );
177    }
178}