async_itertools/features/
zip_longest.rs1use core::{
2 pin::Pin,
3 task::{Context, Poll},
4};
5
6use futures_lite::{Stream, StreamExt, ready, stream::Fuse};
7use option_entry::{Entry, OptionEntry};
8use pin_project_lite::pin_project;
9
10use crate::{EitherOrBoth, internal::check::assert_stream};
11
12pub fn zip_longest<L: Stream, R: Stream>(l: L, r: R) -> crate::ZipLongest<L, R> {
14 assert_stream(ZipLongest {
15 l: l.fuse(),
16 r: r.fuse(),
17 left_item: None,
18 })
19}
20
21pin_project! {
22 pub struct ZipLongest<L, R, Lt = <L as Stream>::Item> {
23 #[pin]
24 l: Fuse<L>,
25 #[pin]
26 r: Fuse<R>,
27 left_item: Option<Lt>,
28 }
29}
30
31impl<L: Stream<Item = Lt>, R: Stream<Item = Rt>, Lt, Rt> Stream for ZipLongest<L, R, Lt> {
32 type Item = EitherOrBoth<Lt, Rt>;
33
34 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
35 let this = self.project();
36 let l = match this.left_item.entry() {
37 Entry::Vacant(entry) => {
38 let Some(l) = ready!(this.l.poll_next(cx)) else {
39 return Poll::Ready(ready!(this.r.poll_next(cx)).map(EitherOrBoth::Right));
40 };
41 entry.insert_entry(l)
42 }
43 Entry::Occupied(entry) => entry,
44 };
45 let Some(r) = ready!(this.r.poll_next(cx)) else {
46 return Poll::Ready(Some(EitherOrBoth::Left(l.remove())));
47 };
48 Poll::Ready(Some(EitherOrBoth::Both(l.remove(), r)))
49 }
50}
51
52#[cfg(test)]
53mod test {
54 use crate::{AsyncItertools, EitherOrBoth};
55
56 #[test]
57 fn left_longer() {
58 let l = futures_lite::stream::iter([1, 2, 3]);
59 let r = futures_lite::stream::iter([1, 2]);
60 assert!(futures_lite::stream::block_on(l.zip_longest(r)).eq([
61 EitherOrBoth::Both(1, 1),
62 EitherOrBoth::Both(2, 2),
63 EitherOrBoth::Left(3),
64 ]));
65 }
66
67 #[test]
68 fn equal_length() {
69 let l = futures_lite::stream::iter([1, 2, 3]);
70 let r = futures_lite::stream::iter([1, 2, 3]);
71 assert!(futures_lite::stream::block_on(l.zip_longest(r)).eq([
72 EitherOrBoth::Both(1, 1),
73 EitherOrBoth::Both(2, 2),
74 EitherOrBoth::Both(3, 3),
75 ]));
76 }
77
78 #[test]
79 fn right_longer() {
80 let l = futures_lite::stream::iter([1, 2]);
81 let r = futures_lite::stream::iter([1, 2, 3]);
82 assert!(futures_lite::stream::block_on(l.zip_longest(r)).eq([
83 EitherOrBoth::Both(1, 1),
84 EitherOrBoth::Both(2, 2),
85 EitherOrBoth::Right(3),
86 ]));
87 }
88}