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}