async_iter_ext/iter.rs
1use std::vec::IntoIter;
2
3use sync_iter::SyncIter;
4
5pub mod process_result;
6pub mod sync_iter;
7
8/// Trait for asynchronous iteration.
9///
10/// This trait is similar to the standard `Iterator` trait, but designed to work in asynchronous
11/// contexts where `next()` returns a `Future`. It provides core methods for driving async iteration,
12/// collecting results, and converting to a synchronous iterator.
13///
14/// Implementors must define `next_async`, which yields the next item wrapped in a `Future`.
15pub trait AsyncIterator {
16 /// The type of the elements being iterated over.
17 type Item;
18
19 /// Asynchronously returns the next item in the iterator.
20 ///
21 /// Returns `None` when iteration is finished.
22 ///
23 /// # Examples
24 ///
25 /// ```rust
26 /// use async_iter_ext::AsyncIterator;
27 ///
28 /// async fn iterate<I: AsyncIterator>(mut iter: I) {
29 /// while let Some(item) = iter.next_async().await {
30 /// // process item
31 /// }
32 /// }
33 /// ```
34 fn next_async(&mut self) -> impl Future<Output = Option<Self::Item>>;
35
36 /// Converts this async iterator into a synchronous [`SyncIter`] using `async` collection.
37 ///
38 /// This method collects all remaining items into a `Vec` and returns a `SyncIter` over them.
39 ///
40 /// ---
41 ///
42 /// # Notes
43 ///
44 /// - This is useful when you want to move from an async context to sync processing.
45 ///
46 /// ---
47 ///
48 /// # Examples
49 ///
50 /// ```rust
51 /// use async_iter_ext::AsyncIterator;
52 ///
53 /// async fn convert<I: AsyncIterator>(iter: &mut I) {
54 /// let sync = iter.sync_iter().await;
55 /// for item in sync {
56 /// // use item
57 /// }
58 /// }
59 /// ```
60 fn sync_iter(&mut self) -> impl Future<Output = SyncIter<IntoIter<Self::Item>>>
61 where
62 Self: Sized,
63 {
64 async move { SyncIter::new(collect_into_vec(self).await.into_iter()) }
65 }
66
67 /// Provides a hint about the size of the remaining items.
68 ///
69 /// Returns a tuple where the first element is a lower bound and the second
70 /// is an optional upper bound.
71 ///
72 /// This is used to optimize certain operations such as preallocating capacity.
73 ///
74 /// Default implementation returns `(0, None)`.
75 fn async_size_hint(&self) -> (usize, Option<usize>) {
76 (0, None)
77 }
78
79 /// Collects all items of the async iterator into a container type.
80 ///
81 /// Works like the standard `Iterator::collect`, but in an async context.
82 ///
83 /// The target container must implement `FromIterator<Self::Item>`.
84 ///
85 /// ---
86 ///
87 /// # Examples
88 ///
89 /// ```rust
90 /// use async_iter_ext::AsyncIterator;
91 ///
92 /// async fn collect_items<I: AsyncIterator>(iter: I) -> Vec<I::Item> {
93 /// iter.async_collect::<Vec<_>>().await
94 /// }
95 /// ```
96 fn async_collect<B>(mut self) -> impl Future<Output = B>
97 where
98 Self: Sized,
99 B: FromIterator<Self::Item>,
100 {
101 async move {
102 let items = collect_into_vec(&mut self).await;
103 B::from_iter(items)
104 }
105 }
106}
107
108/// Asynchronously collects all items from an [`AsyncIterator`] into a `Vec`.
109///
110/// Uses `async_size_hint` to optimize preallocation if the upper bound is known.
111async fn collect_into_vec<I>(iter: &mut I) -> Vec<I::Item>
112where
113 I: AsyncIterator,
114{
115 let mut items = vec![];
116
117 match iter.async_size_hint() {
118 (_, Some(upper_limit)) => {
119 for _ in 0..upper_limit {
120 if let Some(item) = iter.next_async().await {
121 items.push(item);
122 }
123 }
124 }
125 _ => {
126 while let Some(item) = iter.next_async().await {
127 items.push(item);
128 }
129 }
130 }
131
132 items
133}
134
135/// Blanket implementation of [`AsyncIterator`] for all synchronous [`Iterator`] types.
136///
137/// This enables any standard iterator to be used as an async iterator by immediately
138/// returning the next item.
139impl<T> AsyncIterator for T
140where
141 T: Iterator + ?Sized,
142{
143 type Item = T::Item;
144
145 async fn next_async(&mut self) -> Option<Self::Item> {
146 self.next()
147 }
148
149 fn async_size_hint(&self) -> (usize, Option<usize>) {
150 self.size_hint()
151 }
152}