1
1
use proc_macro:: TokenStream ;
2
- use proc_macro2:: { Delimiter , Group , TokenStream as TokenStream2 , TokenTree } ;
2
+ use proc_macro2:: { Group , TokenStream as TokenStream2 , TokenTree } ;
3
3
use quote:: quote;
4
+ use syn:: parse:: Parser ;
4
5
use syn:: visit_mut:: VisitMut ;
5
6
6
- struct Scrub {
7
- is_xforming : bool ,
7
+ struct Scrub < ' a > {
8
+ /// Whether the stream is a try stream.
8
9
is_try : bool ,
10
+ /// The unit expression, `()`.
9
11
unit : Box < syn:: Expr > ,
10
- num_yield : u32 ,
12
+ has_yielded : bool ,
13
+ crate_path : & ' a TokenStream2 ,
11
14
}
12
15
13
- fn parse_input ( input : TokenStream ) -> syn:: Result < Vec < syn:: Stmt > > {
14
- let input = replace_for_await ( input. into ( ) ) ;
15
- // syn does not provide a way to parse `Vec<Stmt>` directly from `TokenStream`,
16
- // so wrap input in a brace and then parse it as a block.
17
- let input = TokenStream2 :: from ( TokenTree :: Group ( Group :: new ( Delimiter :: Brace , input ) ) ) ;
18
- let syn :: Block { stmts , .. } = syn :: parse2 ( input ) ? ;
19
-
20
- Ok ( stmts)
16
+ fn parse_input ( input : TokenStream ) -> syn:: Result < ( TokenStream2 , Vec < syn:: Stmt > ) > {
17
+ let mut input = TokenStream2 :: from ( input) . into_iter ( ) ;
18
+ let crate_path = match input . next ( ) . unwrap ( ) {
19
+ TokenTree :: Group ( group ) => group . stream ( ) ,
20
+ _ => panic ! ( ) ,
21
+ } ;
22
+ let stmts = syn :: Block :: parse_within . parse2 ( replace_for_await ( input ) ) ? ;
23
+ Ok ( ( crate_path , stmts) )
21
24
}
22
25
23
- impl VisitMut for Scrub {
24
- fn visit_expr_mut ( & mut self , i : & mut syn:: Expr ) {
25
- if !self . is_xforming {
26
- syn:: visit_mut:: visit_expr_mut ( self , i) ;
27
- return ;
26
+ impl < ' a > Scrub < ' a > {
27
+ fn new ( is_try : bool , crate_path : & ' a TokenStream2 ) -> Self {
28
+ Self {
29
+ is_try,
30
+ unit : syn:: parse_quote!( ( ) ) ,
31
+ has_yielded : false ,
32
+ crate_path,
28
33
}
34
+ }
35
+ }
29
36
37
+ impl VisitMut for Scrub < ' _ > {
38
+ fn visit_expr_mut ( & mut self , i : & mut syn:: Expr ) {
30
39
match i {
31
40
syn:: Expr :: Yield ( yield_expr) => {
32
- self . num_yield += 1 ;
41
+ self . has_yielded = true ;
33
42
34
- let value_expr = if let Some ( ref e) = yield_expr. expr {
35
- e
36
- } else {
37
- & self . unit
38
- } ;
43
+ let value_expr = yield_expr. expr . as_ref ( ) . unwrap_or ( & self . unit ) ;
39
44
40
45
// let ident = &self.yielder;
41
46
42
47
* i = if self . is_try {
43
- syn:: parse_quote! { __yield_tx. send( Ok ( #value_expr) ) . await }
48
+ syn:: parse_quote! { __yield_tx. send( :: core :: result :: Result :: Ok ( #value_expr) ) . await }
44
49
} else {
45
50
syn:: parse_quote! { __yield_tx. send( #value_expr) . await }
46
51
} ;
@@ -52,19 +57,16 @@ impl VisitMut for Scrub {
52
57
53
58
* i = syn:: parse_quote! {
54
59
match #e {
55
- Ok ( v) => v,
56
- Err ( e) => {
57
- __yield_tx. send( Err ( e. into( ) ) ) . await ;
60
+ :: core :: result :: Result :: Ok ( v) => v,
61
+ :: core :: result :: Result :: Err ( e) => {
62
+ __yield_tx. send( :: core :: result :: Result :: Err ( e. into( ) ) ) . await ;
58
63
return ;
59
64
}
60
65
}
61
66
} ;
62
67
}
63
68
syn:: Expr :: Closure ( _) | syn:: Expr :: Async ( _) => {
64
- let prev = self . is_xforming ;
65
- self . is_xforming = false ;
66
- syn:: visit_mut:: visit_expr_mut ( self , i) ;
67
- self . is_xforming = prev;
69
+ // Don't transform inner closures or async blocks.
68
70
}
69
71
syn:: Expr :: ForLoop ( expr) => {
70
72
syn:: visit_mut:: visit_expr_for_loop_mut ( self , expr) ;
@@ -87,14 +89,15 @@ impl VisitMut for Scrub {
87
89
return ;
88
90
}
89
91
92
+ let crate_path = self . crate_path ;
90
93
* i = syn:: parse_quote! { {
91
94
let mut __pinned = #expr;
92
95
let mut __pinned = unsafe {
93
96
:: core:: pin:: Pin :: new_unchecked( & mut __pinned)
94
97
} ;
95
98
#label
96
99
loop {
97
- let #pat = match :: async_stream :: reexport:: next( & mut __pinned) . await {
100
+ let #pat = match #crate_path :: reexport:: next( & mut __pinned) . await {
98
101
:: core:: option:: Option :: Some ( e) => e,
99
102
:: core:: option:: Option :: None => break ,
100
103
} ;
@@ -106,143 +109,81 @@ impl VisitMut for Scrub {
106
109
}
107
110
}
108
111
109
- fn visit_item_mut ( & mut self , i : & mut syn:: Item ) {
110
- let prev = self . is_xforming ;
111
- self . is_xforming = false ;
112
- syn:: visit_mut:: visit_item_mut ( self , i) ;
113
- self . is_xforming = prev;
112
+ fn visit_item_mut ( & mut self , _: & mut syn:: Item ) {
113
+ // Don't transform inner items.
114
114
}
115
115
}
116
116
117
- /// Asynchronous stream
118
- ///
119
- /// See [crate](index.html) documentation for more details.
120
- ///
121
- /// # Examples
122
- ///
123
- /// ```rust
124
- /// use async_stream::stream;
125
- ///
126
- /// use futures_util::pin_mut;
127
- /// use futures_util::stream::StreamExt;
128
- ///
129
- /// #[tokio::main]
130
- /// async fn main() {
131
- /// let s = stream! {
132
- /// for i in 0..3 {
133
- /// yield i;
134
- /// }
135
- /// };
136
- ///
137
- /// pin_mut!(s); // needed for iteration
138
- ///
139
- /// while let Some(value) = s.next().await {
140
- /// println!("got {}", value);
141
- /// }
142
- /// }
143
- /// ```
117
+ /// The first token tree in the stream must be a group containing the path to the `async-stream`
118
+ /// crate.
144
119
#[ proc_macro]
145
- pub fn stream ( input : TokenStream ) -> TokenStream {
146
- let mut stmts = match parse_input ( input) {
120
+ #[ doc( hidden) ]
121
+ pub fn stream_inner ( input : TokenStream ) -> TokenStream {
122
+ let ( crate_path, mut stmts) = match parse_input ( input) {
147
123
Ok ( x) => x,
148
124
Err ( e) => return e. to_compile_error ( ) . into ( ) ,
149
125
} ;
150
126
151
- let mut scrub = Scrub {
152
- is_xforming : true ,
153
- is_try : false ,
154
- unit : syn:: parse_quote!( ( ) ) ,
155
- num_yield : 0 ,
156
- } ;
127
+ let mut scrub = Scrub :: new ( false , & crate_path) ;
157
128
158
- for mut stmt in & mut stmts[ .. ] {
129
+ for mut stmt in & mut stmts {
159
130
scrub. visit_stmt_mut ( & mut stmt) ;
160
131
}
161
132
162
- let dummy_yield = if scrub. num_yield == 0 {
133
+ let dummy_yield = if scrub. has_yielded {
134
+ None
135
+ } else {
163
136
Some ( quote ! ( if false {
164
137
__yield_tx. send( ( ) ) . await ;
165
138
} ) )
166
- } else {
167
- None
168
139
} ;
169
140
170
141
quote ! ( {
171
- let ( mut __yield_tx, __yield_rx) = :: async_stream :: yielder:: pair( ) ;
172
- :: async_stream :: AsyncStream :: new( __yield_rx, async move {
142
+ let ( mut __yield_tx, __yield_rx) = #crate_path :: yielder:: pair( ) ;
143
+ #crate_path :: AsyncStream :: new( __yield_rx, async move {
173
144
#dummy_yield
174
145
#( #stmts) *
175
146
} )
176
147
} )
177
148
. into ( )
178
149
}
179
150
180
- /// Asynchronous fallible stream
181
- ///
182
- /// See [crate](index.html) documentation for more details.
183
- ///
184
- /// # Examples
185
- ///
186
- /// ```rust
187
- /// use tokio::net::{TcpListener, TcpStream};
188
- ///
189
- /// use async_stream::try_stream;
190
- /// use futures_core::stream::Stream;
191
- ///
192
- /// use std::io;
193
- /// use std::net::SocketAddr;
194
- ///
195
- /// fn bind_and_accept(addr: SocketAddr)
196
- /// -> impl Stream<Item = io::Result<TcpStream>>
197
- /// {
198
- /// try_stream! {
199
- /// let mut listener = TcpListener::bind(addr).await?;
200
- ///
201
- /// loop {
202
- /// let (stream, addr) = listener.accept().await?;
203
- /// println!("received on {:?}", addr);
204
- /// yield stream;
205
- /// }
206
- /// }
207
- /// }
208
- /// ```
151
+ /// The first token tree in the stream must be a group containing the path to the `async-stream`
152
+ /// crate.
209
153
#[ proc_macro]
210
- pub fn try_stream ( input : TokenStream ) -> TokenStream {
211
- let mut stmts = match parse_input ( input) {
154
+ #[ doc( hidden) ]
155
+ pub fn try_stream_inner ( input : TokenStream ) -> TokenStream {
156
+ let ( crate_path, mut stmts) = match parse_input ( input) {
212
157
Ok ( x) => x,
213
158
Err ( e) => return e. to_compile_error ( ) . into ( ) ,
214
159
} ;
215
160
216
- let mut scrub = Scrub {
217
- is_xforming : true ,
218
- is_try : true ,
219
- unit : syn:: parse_quote!( ( ) ) ,
220
- num_yield : 0 ,
221
- } ;
161
+ let mut scrub = Scrub :: new ( true , & crate_path) ;
222
162
223
- for mut stmt in & mut stmts[ .. ] {
163
+ for mut stmt in & mut stmts {
224
164
scrub. visit_stmt_mut ( & mut stmt) ;
225
165
}
226
166
227
- let dummy_yield = if scrub. num_yield == 0 {
167
+ let dummy_yield = if scrub. has_yielded {
168
+ None
169
+ } else {
228
170
Some ( quote ! ( if false {
229
171
__yield_tx. send( ( ) ) . await ;
230
172
} ) )
231
- } else {
232
- None
233
173
} ;
234
174
235
175
quote ! ( {
236
- let ( mut __yield_tx, __yield_rx) = :: async_stream :: yielder:: pair( ) ;
237
- :: async_stream :: AsyncStream :: new( __yield_rx, async move {
176
+ let ( mut __yield_tx, __yield_rx) = #crate_path :: yielder:: pair( ) ;
177
+ #crate_path :: AsyncStream :: new( __yield_rx, async move {
238
178
#dummy_yield
239
179
#( #stmts) *
240
180
} )
241
181
} )
242
182
. into ( )
243
183
}
244
184
245
- fn replace_for_await ( input : TokenStream2 ) -> TokenStream2 {
185
+ /// Replace `for await` with `#[await] for`, which will be later transformed into a `next` loop.
186
+ fn replace_for_await ( input : impl IntoIterator < Item = TokenTree > ) -> TokenStream2 {
246
187
let mut input = input. into_iter ( ) . peekable ( ) ;
247
188
let mut tokens = Vec :: new ( ) ;
248
189
0 commit comments