1
- use futures:: stream:: Stream ;
1
+ use futures:: stream:: { Fuse , FusedStream , Stream , StreamExt } ;
2
2
use paste:: paste;
3
+ use pin_project_lite:: pin_project;
3
4
use std:: pin:: Pin ;
4
5
use std:: task:: { Context , Poll } ;
5
6
6
7
macro_rules! combine_latest {
7
8
( $name: ident; $( $stream: ident) ,+; $( $type: ident) ,+) => {
8
9
paste! {
9
- pub struct $name<$( $stream: Stream <Item = $type>) ,+, $( $type) ,+> {
10
- $(
11
- [ <$stream: lower>] : Pin <Box <$stream>>,
12
- [ <$type: lower>] : Option <$type>,
13
- ) +
10
+ pin_project! {
11
+ pub struct $name<$( $stream: Stream <Item = $type>) ,+, $( $type) ,+> {
12
+ $(
13
+ #[ pin]
14
+ [ <$stream: lower>] : Fuse <$stream>,
15
+ [ <$type: lower>] : Option <$type>,
16
+ ) +
17
+ }
14
18
}
15
19
}
16
20
@@ -22,61 +26,70 @@ macro_rules! combine_latest {
22
26
) -> Self {
23
27
$name {
24
28
$(
25
- [ <$stream: lower>] : Box :: pin ( [ <$stream: lower>] ) ,
29
+ [ <$stream: lower>] : [ <$stream: lower>] . fuse ( ) ,
26
30
[ <$type: lower>] : None ,
27
31
) +
28
32
}
29
33
}
30
34
}
31
35
}
32
36
33
- impl <$( $stream: Stream <Item = $type>) ,+, $( $type: Clone + Unpin ) ,+> Stream for $name<$( $stream) ,+, $( $type) ,+>
37
+ impl <$( $stream: Stream <Item = $type>) ,+, $( $type: ToOwned <Owned = $type>) ,+> FusedStream for $name<$( $stream) ,+, $( $type) ,+>
38
+ {
39
+ fn is_terminated( & self ) -> bool {
40
+ paste! {
41
+ $(
42
+ self . [ <$stream: lower>] . is_terminated( )
43
+ ) &&+
44
+ }
45
+ }
46
+ }
47
+
48
+ impl <$( $stream: Stream <Item = $type>) ,+, $( $type: ToOwned <Owned = $type>) ,+> Stream for $name<$( $stream) ,+, $( $type) ,+>
34
49
{
35
50
type Item = ( $( $type) ,+) ;
36
51
37
52
fn poll_next( self : Pin <& mut Self >, cx: & mut Context <' _>) -> Poll <Option <Self :: Item >> {
38
- let this = self . get_mut ( ) ;
53
+ let mut this = self . project ( ) ;
39
54
40
55
fn poll_next<S : Stream <Item = T >, T >(
41
- stream: & mut Pin < Box <S >>,
56
+ stream: Pin < & mut Fuse <S >>,
42
57
cx: & mut Context <' _>,
43
- ) -> ( Option <T >, bool ) {
44
- match stream. as_mut( ) . poll_next( cx) {
45
- Poll :: Ready ( Some ( it) ) => ( Some ( it) , false ) ,
46
- Poll :: Ready ( None ) => ( None , true ) ,
47
- Poll :: Pending => ( None , false ) ,
58
+ ) -> Option <T > {
59
+ match stream. poll_next( cx) {
60
+ Poll :: Ready ( Some ( it) ) => Some ( it) ,
61
+ _ => None ,
48
62
}
49
63
}
50
64
51
65
paste! {
66
+ let mut did_update_value = false ;
52
67
$(
53
- let ( [ <event_ $stream: lower>] , [ <is_done_ $stream: lower>] ) = poll_next( & mut this. [ <$stream: lower>] , cx) ;
54
- ) +
55
- let did_update_value = $(
56
- [ <event_ $stream: lower>] . is_some( )
57
- ) ||+;
58
- $(
59
- if [ <event_ $stream: lower>] . is_some( ) {
60
- this. [ <$type: lower>] = [ <event_ $stream: lower>] ;
61
- }
62
- ) +
63
- let all_done = $(
64
- [ <is_done_ $stream: lower>]
65
- ) &&+;
66
- let all_emitted = $(
67
- this. [ <$type: lower>] . is_some( )
68
- ) &&+;
68
+ if !this. [ <$stream: lower>] . is_done( ) {
69
+ let next = poll_next( this. [ <$stream: lower>] . as_mut( ) , cx) ;
70
+
71
+ if !did_update_value {
72
+ did_update_value = next. is_some( ) ;
73
+ }
69
74
70
- let should_emit_next = all_emitted && did_update_value;
75
+ if next. is_some( ) {
76
+ * this. [ <$type: lower>] = next;
77
+ }
78
+ } ;
79
+ ) +
80
+ let should_emit_next = did_update_value && $( this. [ <$type: lower>] . is_some( ) ) &&+;
71
81
72
- match ( all_done , should_emit_next) {
73
- ( true , _ ) => Poll :: Ready ( None ) ,
74
- ( false , true ) => Poll :: Ready ( Some ( (
82
+ if should_emit_next {
83
+ // maybe to_owned can be avoided? Event/Rc?
84
+ Poll :: Ready ( Some ( (
75
85
$(
76
86
this. [ <$type: lower>] . as_ref( ) . unwrap( ) . to_owned( )
77
87
) ,+
78
- ) ) ) ,
79
- _ => Poll :: Pending ,
88
+ ) ) )
89
+ } else if $( this. [ <$stream: lower>] . is_done( ) ) &&+ {
90
+ Poll :: Ready ( None )
91
+ } else {
92
+ Poll :: Pending
80
93
}
81
94
}
82
95
}
0 commit comments