async_iter_ext/lib.rs
1#![doc = include_str!("../README.md")]
2
3use combinator::{filter::AsyncFilter, map::AsyncMap};
4
5pub mod combinator;
6pub mod iter;
7mod option;
8mod result;
9
10pub use iter::AsyncIterator;
11pub use option::AsyncOptionTools;
12pub use result::AsyncResultTools;
13
14use crate::iter::process_result::ProcessResults;
15
16pub trait AsyncIterTools: AsyncIterator {
17 /// Calls an async closure on each element of an iterator
18 ///
19 /// This is equivalent to using a for loop on the iterator, although break and continue are not possible from a closure.
20 ///
21 /// ---
22 ///
23 /// # Examples
24 ///
25 /// Basic usage:
26 ///
27 /// ```rust
28 /// use std::time::Duration;
29 ///
30 /// use async_iter_ext::AsyncIterTools;
31 /// use async_std::task;
32 ///
33 /// task::block_on(async {
34 /// let items = [1, 2, 3, 4];
35 /// items
36 /// .iter()
37 /// .for_each_async(|item| {
38 /// async move {
39 /// // Simulate some async work
40 /// task::sleep(Duration::from_millis(100)).await;
41 /// println!("Processing {}", item);
42 /// }
43 /// })
44 /// .await;
45 /// });
46 /// ```
47 fn for_each_async<F, Fut>(mut self, f: F) -> impl Future<Output = ()>
48 where
49 Self: Sized,
50 F: Fn(Self::Item) -> Fut,
51 Fut: Future<Output = ()>,
52 {
53 async move {
54 while let Some(item) = self.next_async().await {
55 f(item).await;
56 }
57 }
58 }
59
60 /// Applies an async closure to each item of the iterator, returning a new iterator
61 /// of the results of each async computation.
62 ///
63 /// This is similar to the standard `Iterator::map` method, but works with asynchronous
64 /// closures that return `Future`s.
65 ///
66 /// ---
67 ///
68 /// # Examples
69 ///
70 /// ```rust
71 /// use std::time::Duration;
72 ///
73 /// use async_iter_ext::{AsyncIterTools, AsyncIterator};
74 /// use async_std::task;
75 ///
76 /// let multiplied_by_two = task::block_on(async {
77 /// let items = [1, 2, 3, 4];
78 /// items
79 /// .iter()
80 /// .map_async(|item| {
81 /// async move {
82 /// // Simulate async transformation
83 /// task::sleep(Duration::from_millis(100)).await;
84 /// item * 2
85 /// }
86 /// })
87 /// .async_collect::<Vec<_>>()
88 /// .await
89 /// });
90 /// ```
91 fn map_async<B, F, Fut>(self, f: F) -> AsyncMap<Self, F>
92 where
93 Self: Sized,
94 F: Fn(Self::Item) -> Fut,
95 Fut: Future<Output = B>,
96 {
97 AsyncMap { iter: self, f }
98 }
99
100 /// Filters the items of an iterator using an asynchronous predicate.
101 ///
102 /// This works like the standard `Iterator::filter`, but allows the predicate
103 /// to be asynchronous by returning a `Future<Output = bool>`.
104 ///
105 /// ---
106 ///
107 /// > ⚠️ Warning: The item type must implement `Clone` because filtering requires that the item is cloned when the predicate is run.
108 ///
109 /// ---
110 ///
111 /// # Examples
112 ///
113 /// ```rust
114 /// use async_iter_ext::{AsyncIterTools, AsyncIterator};
115 /// use async_std::task;
116 ///
117 /// task::block_on(async {
118 /// let items = [1, 2, 3, 4, 5, 6];
119 /// let filtered = items
120 /// .iter()
121 /// .filter_async(|item| {
122 /// async move {
123 /// // Keep even numbers only
124 /// item % 2 == 0
125 /// }
126 /// })
127 /// .async_collect::<Vec<_>>();
128 /// });
129 /// ```
130 fn filter_async<F, Fut>(self, f: F) -> AsyncFilter<Self, F>
131 where
132 Self: Sized,
133 F: Fn(Self::Item) -> Fut,
134 Fut: Future<Output = bool>,
135 Self::Item: Clone,
136 {
137 AsyncFilter { iter: self, f }
138 }
139
140 /// Consumes the async iterator and returns a `ProcessResults` future that collects
141 /// successes and errors based on a specified strategy.
142 ///
143 /// This is a convenience method for turning an `AsyncIterator<Item = Result<T, E>>`
144 /// into a `ProcessResults`, which is a future that resolves to a
145 /// `ProcessResultsContainer<T, E>`.
146 ///
147 /// By default, the `ProcessResultsStrategy::Partition` strategy is used,
148 /// which means all items will be processed and separated into successes and errors.
149 /// You can change the strategy using `.with_process_strategy(...)`.
150 ///
151 /// # Type Parameters
152 /// - `T`: The success type inside the `Result`.
153 /// - `E`: The error type inside the `Result`.
154 ///
155 /// # Returns
156 /// A `ProcessResults<Self, T, E>` future that resolves to `ProcessResultsContainer<T, E>`.
157 ///
158 /// # Examples
159 ///
160 /// ```rust
161 /// use async_iter_ext::{AsyncIterTools, AsyncIterator, iter::process_result::ProcessResultsStrategy};
162 /// use async_std::task;
163 ///
164 /// task::block_on(async {
165 /// // Partition strategy: collect all successes and errors
166 /// let results = vec![Ok(1), Err("err1"), Ok(2), Err("err2")]
167 /// .into_iter()
168 /// .process_results::<i32, &str>()
169 /// .await;
170 /// assert_eq!(results.successes(), &vec![1, 2]);
171 /// assert_eq!(results.errors(), &vec!["err1", "err2"]);
172 ///
173 /// // Convert to result and unwrap error. OBS: Only gets the first error
174 /// let first_result_err = results.into_result().unwrap_err();
175 /// assert_eq!(first_result_err, "err1");
176 ///
177 /// // Partition strategy: collect all successes
178 /// let results = vec![Ok(1), Ok(2)].into_iter().process_results::<i32, &str>().await;
179 /// assert_eq!(results.successes(), &vec![1, 2]);
180 ///
181 /// // Convert to result and unwrap
182 /// let successes = results.into_result().unwrap();
183 /// assert_eq!(successes, vec![1, 2]);
184 ///
185 /// // BreakOnError strategy: stop at the first error
186 /// let results = vec![Ok(1), Err("early"), Ok(2)]
187 /// .into_iter()
188 /// .process_results::<i32, &str>()
189 /// .with_process_strategy(ProcessResultsStrategy::BreakOnError)
190 /// .await;
191 /// assert_eq!(results.successes(), &vec![]); // did not continue after first error
192 /// assert_eq!(results.errors(), &vec!["early"]);
193 /// });
194 /// ```
195 fn process_results<T, E>(self) -> ProcessResults<Self, T, E>
196 where
197 Self: Sized + AsyncIterator<Item = Result<T, E>>,
198 {
199 ProcessResults::new(self)
200 }
201}
202
203impl<T> AsyncIterTools for T where T: AsyncIterator + ?Sized {}