iter_enum/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*!
4<!-- tidy:crate-doc:start -->
5\#\[derive(Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, Extend)\] for enums.
6
7## Usage
8
9Add this to your `Cargo.toml`:
10
11```toml
12[dependencies]
13iter-enum = "1"
14```
15
16## Examples
17
18```rust
19use iter_enum::*;
20
21#[derive(Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, Extend)]
22enum Either<A, B> {
23    A(A),
24    B(B),
25}
26
27fn foo(x: i32) -> impl Iterator<Item = i32> {
28    if x > 0 {
29        Either::A(x..=0)
30    } else {
31        Either::B(Some(x).into_iter())
32    }
33}
34```
35
36See [auto_enums] crate for how to automate patterns like this.
37
38## Supported traits
39
40- [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/iterator.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/iterator.expanded.rs)
41- [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/double_ended_iterator.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/double_ended_iterator.expanded.rs)
42- [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/exact_size_iterator.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/exact_size_iterator.expanded.rs)
43- [`FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/fused_iterator.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/fused_iterator.expanded.rs)
44- [`Extend`](https://doc.rust-lang.org/std/iter/trait.Extend.html) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/extend.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/extend.expanded.rs)
45- [`ParallelIterator`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html) (*requires `"rayon"` feature*) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/parallel_iterator.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/parallel_iterator.expanded.rs)
46- [`IndexedParallelIterator`](https://docs.rs/rayon/latest/rayon/iter/trait.IndexedParallelIterator.html) (*requires `"rayon"` feature*) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/indexed_parallel_iterator.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/indexed_parallel_iterator.expanded.rs)
47- [`ParallelExtend`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelExtend.html) (*requires `"rayon"` feature*) - [example](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/parallel_extend.rs) | [generated code](https://github.com/taiki-e/iter-enum/blob/HEAD/tests/expand/parallel_extend.expanded.rs)
48
49## Optional features
50
51- **`rayon`**
52  - Enable to use `#[derive(ParallelIterator, IndexedParallelIterator, ParallelExtend)]`.
53
54## Related Projects
55
56- [auto_enums]: A library for to allow multiple return types by automatically generated enum.
57- [derive_utils]: A procedural macro helper for easily writing [derives macros][proc-macro-derive] for enums.
58- [io-enum]: \#\[derive(Read, Write, Seek, BufRead)\] for enums.
59
60[auto_enums]: https://github.com/taiki-e/auto_enums
61[derive_utils]: https://github.com/taiki-e/derive_utils
62[io-enum]: https://github.com/taiki-e/io-enum
63[proc-macro-derive]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros
64
65<!-- tidy:crate-doc:end -->
66*/
67
68#![doc(test(
69    no_crate_inject,
70    attr(
71        deny(warnings, rust_2018_idioms, single_use_lifetimes),
72        allow(dead_code, unused_variables)
73    )
74))]
75#![forbid(unsafe_code)]
76// docs.rs only (cfg is enabled by docs.rs, not build script)
77#![cfg_attr(docsrs, feature(doc_cfg))]
78
79use derive_utils::quick_derive;
80use proc_macro::TokenStream;
81
82#[proc_macro_derive(Iterator)]
83pub fn derive_iterator(input: TokenStream) -> TokenStream {
84    // TODO: Add try_fold once try_trait_v2 is stabilized https://github.com/rust-lang/rust/issues/84277
85    quick_derive! {
86        input,
87        ::core::iter::Iterator,
88        trait Iterator {
89            type Item;
90            #[inline]
91            fn next(&mut self) -> ::core::option::Option<Self::Item>;
92            #[inline]
93            fn size_hint(&self) -> (usize, ::core::option::Option<usize>);
94            #[inline]
95            fn count(self) -> usize;
96            #[inline]
97            fn last(self) -> ::core::option::Option<Self::Item>;
98            #[inline]
99            fn nth(&mut self, n: usize) -> ::core::option::Option<Self::Item>;
100            #[inline]
101            #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"]
102            fn collect<__U: ::core::iter::FromIterator<Self::Item>>(self) -> __U;
103            #[inline]
104            fn partition<__U, __F>(self, f: __F) -> (__U, __U)
105            where
106                __U: ::core::default::Default + ::core::iter::Extend<Self::Item>,
107                __F: ::core::ops::FnMut(&Self::Item) -> bool;
108
109            // Once try_trait_v2 is stabilized, we can replace these by implementing try_fold.
110            #[inline]
111            fn fold<__U, __F>(self, init: __U, f: __F) -> __U
112            where
113                __F: ::core::ops::FnMut(__U, Self::Item) -> __U;
114            #[inline]
115            fn all<__F>(&mut self, f: __F) -> bool
116            where
117                __F: ::core::ops::FnMut(Self::Item) -> bool;
118            #[inline]
119            fn any<__F>(&mut self, f: __F) -> bool
120            where
121                __F: ::core::ops::FnMut(Self::Item) -> bool;
122            #[inline]
123            fn find<__P>(&mut self, predicate: __P) -> ::core::option::Option<Self::Item>
124            where
125                __P: ::core::ops::FnMut(&Self::Item) -> bool;
126            #[inline]
127            fn find_map<__U, __F>(&mut self, f: __F) -> ::core::option::Option<__U>
128            where
129                __F: ::core::ops::FnMut(Self::Item) -> ::core::option::Option<__U>;
130            #[inline]
131            fn position<__P>(&mut self, predicate: __P) -> ::core::option::Option<usize>
132            where
133                __P: ::core::ops::FnMut(Self::Item) -> bool;
134        }
135    }
136}
137
138#[proc_macro_derive(DoubleEndedIterator)]
139pub fn derive_double_ended_iterator(input: TokenStream) -> TokenStream {
140    // TODO: Add try_rfold once try_trait_v2 is stabilized https://github.com/rust-lang/rust/issues/84277
141    // TODO: Add advance_back_by once stabilized https://github.com/rust-lang/rust/issues/77404
142    quick_derive! {
143        input,
144        ::core::iter::DoubleEndedIterator,
145        <Item>,
146        trait DoubleEndedIterator: ::core::iter::Iterator {
147            #[inline]
148            fn next_back(&mut self) -> ::core::option::Option<Self::Item>;
149            #[inline]
150            fn nth_back(&mut self, n: usize) -> ::core::option::Option<Self::Item>;
151
152            // Once try_trait_v2 is stabilized, we can replace these by implementing try_rfold.
153            #[inline]
154            fn rfold<__U, __F>(self, init: __U, f: __F) -> __U
155            where
156                __F: ::core::ops::FnMut(__U, Self::Item) -> __U;
157            #[inline]
158            fn rfind<__P>(&mut self, predicate: __P) -> ::core::option::Option<Self::Item>
159            where
160                __P: ::core::ops::FnMut(&Self::Item) -> bool;
161        }
162    }
163}
164
165#[proc_macro_derive(ExactSizeIterator)]
166pub fn derive_exact_size_iterator(input: TokenStream) -> TokenStream {
167    // TODO: Add is_empty once stabilized https://github.com/rust-lang/rust/issues/35428
168    quick_derive! {
169        input,
170        ::core::iter::ExactSizeIterator,
171        <Item>,
172        trait ExactSizeIterator: ::core::iter::Iterator {
173            #[inline]
174            fn len(&self) -> usize;
175        }
176    }
177}
178
179#[proc_macro_derive(FusedIterator)]
180pub fn derive_fused_iterator(input: TokenStream) -> TokenStream {
181    quick_derive! {
182        input,
183        ::core::iter::FusedIterator,
184        <Item>,
185        trait FusedIterator: ::core::iter::Iterator {}
186    }
187}
188
189#[proc_macro_derive(Extend)]
190pub fn derive_extend(input: TokenStream) -> TokenStream {
191    // TODO: Add extend_one,extend_reserve once stabilized https://github.com/rust-lang/rust/issues/72631
192    quick_derive! {
193        input,
194        ::core::iter::Extend,
195        trait Extend<__A> {
196            #[inline]
197            fn extend<__T: ::core::iter::IntoIterator<Item = __A>>(&mut self, iter: __T);
198        }
199    }
200}
201
202#[cfg(feature = "rayon")]
203#[cfg_attr(docsrs, doc(cfg(feature = "rayon")))]
204#[proc_macro_derive(ParallelIterator)]
205pub fn derive_parallel_iterator(input: TokenStream) -> TokenStream {
206    quick_derive! {
207        input,
208        ::rayon::iter::ParallelIterator,
209        trait ParallelIterator {
210            type Item;
211            #[inline]
212            fn drive_unindexed<__C>(self, consumer: __C) -> __C::Result
213            where
214                __C: ::rayon::iter::plumbing::UnindexedConsumer<Self::Item>;
215            #[inline]
216            fn opt_len(&self) -> ::core::option::Option<usize>;
217        }
218    }
219}
220
221#[cfg(feature = "rayon")]
222#[cfg_attr(docsrs, doc(cfg(feature = "rayon")))]
223#[proc_macro_derive(IndexedParallelIterator)]
224pub fn derive_indexed_parallel_iterator(input: TokenStream) -> TokenStream {
225    quick_derive! {
226        input,
227        ::rayon::iter::IndexedParallelIterator,
228        <Item>,
229        trait IndexedParallelIterator: ::rayon::iter::ParallelIterator {
230            #[inline]
231            fn drive<__C>(self, consumer: __C) -> __C::Result
232            where
233                __C: ::rayon::iter::plumbing::Consumer<Self::Item>;
234            #[inline]
235            fn len(&self) -> usize;
236            #[inline]
237            fn with_producer<__CB>(self, callback: __CB) -> __CB::Output
238            where
239                __CB: ::rayon::iter::plumbing::ProducerCallback<Self::Item>;
240        }
241    }
242}
243
244#[cfg(feature = "rayon")]
245#[cfg_attr(docsrs, doc(cfg(feature = "rayon")))]
246#[proc_macro_derive(ParallelExtend)]
247pub fn derive_parallel_extend(input: TokenStream) -> TokenStream {
248    quick_derive! {
249        input,
250        ::rayon::iter::ParallelExtend,
251        trait ParallelExtend<__T: ::core::marker::Send> {
252            #[inline]
253            fn par_extend<__I>(&mut self, par_iter: __I)
254            where
255                __I: ::rayon::iter::IntoParallelIterator<Item = __T>;
256        }
257    }
258}