1#![deny(missing_docs)]
10#![allow(clippy::let_unit_value)]
11
12use {
15 futures::{
16 channel::mpsc,
17 future::FusedFuture,
18 prelude::*,
19 stream::FusedStream,
20 task::{Context, Poll},
21 },
22 pin_project::pin_project,
23 std::pin::Pin,
24};
25
26pub fn generate<'a, I, R, C, F>(cb: C) -> Generator<F, I, R>
33where
34 C: FnOnce(Yield<I>) -> F,
35 F: Future<Output = R> + 'a,
36 I: Send + 'static,
37 R: Send + 'static,
38{
39 let (send, recv) = mpsc::channel(0);
40 Generator { task: cb(Yield(send)).fuse(), stream: recv, res: None }
41}
42
43pub struct Yield<I>(mpsc::Sender<I>);
45
46impl<I> Yield<I>
47where
48 I: Send + 'static,
49{
50 pub fn yield_(&mut self, item: I) -> impl Future<Output = ()> + '_ {
52 self.0.send(item).map(|_| ())
54 }
55
56 pub fn yield_all<S>(&mut self, items: S) -> impl Future<Output = ()> + '_
58 where
59 S: IntoIterator<Item = I>,
60 S::IntoIter: 'static,
61 {
62 let mut items = futures::stream::iter(items.into_iter().map(Ok));
63 async move {
64 let _ = self.0.send_all(&mut items).await;
65 }
66 }
67}
68
69#[derive(Debug, PartialEq, Eq)]
71pub enum GeneratorState<I, R> {
72 Yielded(I),
74
75 Complete(R),
77}
78
79impl<I, R> GeneratorState<I, R> {
80 fn into_yielded(self) -> Option<I> {
81 match self {
82 GeneratorState::Yielded(item) => Some(item),
83 _ => None,
84 }
85 }
86
87 fn into_complete(self) -> Option<R> {
88 match self {
89 GeneratorState::Complete(res) => Some(res),
90 _ => None,
91 }
92 }
93}
94
95#[pin_project]
97#[derive(Debug)]
98pub struct Generator<F, I, R>
99where
100 F: Future<Output = R>,
101{
102 #[pin]
103 task: future::Fuse<F>,
104 #[pin]
105 stream: mpsc::Receiver<I>,
106 res: Option<R>,
107}
108
109impl<F, I, E> Generator<F, I, Result<(), E>>
110where
111 F: Future<Output = Result<(), E>>,
112{
113 pub fn into_try_stream(self) -> impl FusedStream<Item = Result<I, E>> {
115 self.filter_map(|state| {
116 future::ready(match state {
117 GeneratorState::Yielded(i) => Some(Ok(i)),
118 GeneratorState::Complete(Ok(())) => None,
119 GeneratorState::Complete(Err(e)) => Some(Err(e)),
120 })
121 })
122 }
123}
124
125impl<F, I, R> Generator<F, I, R>
126where
127 F: Future<Output = R>,
128{
129 pub async fn into_complete(self) -> R {
131 let s = self.filter_map(|state| future::ready(state.into_complete()));
132 futures::pin_mut!(s);
133
134 s.next().await.unwrap()
137 }
138}
139
140impl<F, I> Generator<F, I, ()>
141where
142 F: Future<Output = ()>,
143{
144 pub fn into_yielded(self) -> impl FusedStream<Item = I> {
147 self.filter_map(|state| future::ready(state.into_yielded()))
148 }
149}
150
151impl<F, I, R> Stream for Generator<F, I, R>
152where
153 F: Future<Output = R>,
154{
155 type Item = GeneratorState<I, R>;
156
157 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
158 let this = self.project();
159
160 let mut task_done = this.task.is_terminated();
163 if let Poll::Ready(res) = this.task.poll(cx) {
164 this.res.replace(res);
166 task_done = true;
167 }
168
169 if !this.stream.is_terminated() {
172 match this.stream.poll_next(cx) {
173 Poll::Pending => return Poll::Pending,
174 Poll::Ready(Some(item)) => return Poll::Ready(Some(GeneratorState::Yielded(item))),
175 Poll::Ready(None) => {}
176 }
177 }
178
179 if !task_done {
180 return Poll::Pending;
181 }
182
183 match this.res.take() {
185 Some(res) => Poll::Ready(Some(GeneratorState::Complete(res))),
186 None => Poll::Ready(None),
187 }
188 }
189}
190
191impl<F, I, R> FusedStream for Generator<F, I, R>
192where
193 F: Future<Output = R>,
194{
195 fn is_terminated(&self) -> bool {
196 self.task.is_terminated() && self.stream.is_terminated() && self.res.is_none()
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use futures::executor::block_on;
204 use std::sync::atomic;
205
206 fn yield_once() -> impl Future<Output = ()> {
208 let mut done = false;
209 future::poll_fn(move |cx: &mut Context<'_>| {
210 if !done {
211 done = true;
212 cx.waker().wake_by_ref();
213 Poll::Pending
214 } else {
215 Poll::Ready(())
216 }
217 })
218 }
219
220 #[derive(Debug, Default)]
221 struct Counter(atomic::AtomicU32);
222
223 impl Counter {
224 fn inc(&self) {
225 self.0.fetch_add(1, atomic::Ordering::SeqCst);
226 }
227
228 fn take(&self) -> u32 {
229 self.0.swap(0, atomic::Ordering::SeqCst)
230 }
231 }
232
233 #[test]
234 fn generator_waits_for_item_to_yield() {
235 let counter = Counter::default();
236
237 let s = generate(|mut co| {
238 let counter = &counter;
239 async move {
240 counter.inc();
241 co.yield_("first").await;
242
243 counter.inc();
246 yield_once().await;
247
248 counter.inc();
249 co.yield_("second").await;
250
251 drop(co);
252 yield_once().await;
253
254 counter.inc();
255 }
256 });
257
258 block_on(async {
259 futures::pin_mut!(s);
260
261 assert_eq!(counter.take(), 0);
262
263 assert_eq!(s.next().await, Some(GeneratorState::Yielded("first")));
264 assert_eq!(counter.take(), 1);
265
266 assert_eq!(s.next().await, Some(GeneratorState::Yielded("second")));
267 assert_eq!(counter.take(), 2);
268
269 assert_eq!(s.next().await, Some(GeneratorState::Complete(())));
270 assert_eq!(counter.take(), 1);
271
272 assert_eq!(s.next().await, None);
273 assert_eq!(counter.take(), 0);
274 });
275 }
276
277 #[test]
278 fn yield_all_yields_all() {
279 let s = generate(|mut co| async move {
280 co.yield_all(1u32..4).await;
281 co.yield_(42).await;
282 });
283
284 let res = block_on(s.collect::<Vec<GeneratorState<u32, ()>>>());
285
286 assert_eq!(
287 res,
288 vec![
289 GeneratorState::Yielded(1),
290 GeneratorState::Yielded(2),
291 GeneratorState::Yielded(3),
292 GeneratorState::Yielded(42),
293 GeneratorState::Complete(()),
294 ]
295 );
296 }
297
298 #[test]
299 fn fused_impl() {
300 let s = generate(|mut co| async move {
301 co.yield_(1u32).await;
302 drop(co);
303
304 yield_once().await;
305
306 "done"
307 });
308
309 block_on(async {
310 futures::pin_mut!(s);
311
312 assert!(!s.is_terminated());
313 assert_eq!(s.next().await, Some(GeneratorState::Yielded(1)));
314
315 assert!(!s.is_terminated());
316 assert_eq!(s.next().await, Some(GeneratorState::Complete("done")));
317
318 assert!(s.is_terminated());
322 assert_eq!(s.next().await, None);
323
324 assert!(s.is_terminated());
325 });
326 }
327
328 #[test]
329 fn into_try_stream_transposes_generator_states() {
330 let s = generate(|mut co| async move {
331 co.yield_(1u8).await;
332 co.yield_(2u8).await;
333
334 Result::<(), &'static str>::Err("oops")
335 })
336 .into_try_stream();
337
338 let res = block_on(s.collect::<Vec<Result<u8, &'static str>>>());
339
340 assert_eq!(res, vec![Ok(1), Ok(2), Err("oops")]);
341 }
342
343 #[test]
344 fn into_try_stream_eats_unit_success() {
345 let s = generate(|mut co| async move {
346 co.yield_(1u8).await;
347 co.yield_(2u8).await;
348
349 Result::<(), &'static str>::Ok(())
350 })
351 .into_try_stream();
352
353 let res = block_on(s.collect::<Vec<Result<u8, &'static str>>>());
354
355 assert_eq!(res, vec![Ok(1), Ok(2)]);
356 }
357
358 #[test]
359 fn runs_task_to_completion() {
360 let finished = Counter::default();
361
362 let make_s = || {
363 generate(|mut co| async {
364 co.yield_(8u8).await;
365
366 drop(co);
368 yield_once().await;
369
370 finished.inc();
371 })
372 };
373
374 block_on(async {
377 let res = make_s().collect::<Vec<GeneratorState<u8, ()>>>().await;
378 assert_eq!(res, vec![GeneratorState::Yielded(8), GeneratorState::Complete(())]);
379 assert_eq!(finished.take(), 1);
380 });
381
382 block_on(async {
383 assert_eq!(make_s().into_yielded().collect::<Vec<_>>().await, vec![8]);
384 assert_eq!(finished.take(), 1);
385 });
386
387 block_on(async {
388 let () = make_s().into_complete().await;
389 assert_eq!(finished.take(), 1);
390 });
391 }
392
393 #[test]
394 fn fibonacci() {
395 let fib = generate(|mut co| async move {
396 let (mut a, mut b) = (0u32, 1u32);
397 loop {
398 co.yield_(a).await;
399
400 let n = b;
401 b += a;
402 a = n;
403 }
404 })
405 .into_yielded()
406 .take(10)
407 .collect::<Vec<_>>();
408
409 assert_eq!(block_on(fib), vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]);
410 }
411}