-
Notifications
You must be signed in to change notification settings - Fork 629
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
219 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::{format_ident, quote, ToTokens}; | ||
use syn::{parse::Parser, punctuated::Punctuated, Expr, Index, Token}; | ||
|
||
/// The `stream_select!` macro. | ||
pub(crate) fn stream_select(input: TokenStream) -> Result<TokenStream, syn::Error> { | ||
let args = Punctuated::<Expr, Token![,]>::parse_terminated.parse2(input)?; | ||
if args.len() < 2 { | ||
return Ok(quote! { | ||
compile_error!("stream select macro needs at least two arguments.") | ||
}); | ||
} | ||
let generic_idents = (0..args.len()).map(|i| format_ident!("_{}", i)).collect::<Vec<_>>(); | ||
let field_idents = (0..args.len()).map(|i| format_ident!("__{}", i)).collect::<Vec<_>>(); | ||
let field_idents_2 = (0..args.len()).map(|i| format_ident!("___{}", i)).collect::<Vec<_>>(); | ||
let field_indices = (0..args.len()).map(Index::from).collect::<Vec<_>>(); | ||
let args = args.iter().map(|e| e.to_token_stream()); | ||
|
||
Ok(quote! { | ||
{ | ||
#[derive(Debug)] | ||
struct StreamSelect<#(#generic_idents),*> (#(Option<#generic_idents>),*); | ||
|
||
enum StreamEnum<#(#generic_idents),*> { | ||
#( | ||
#generic_idents(#generic_idents) | ||
),*, | ||
None, | ||
} | ||
|
||
impl<ITEM, #(#generic_idents),*> __futures_crate::stream::Stream for StreamEnum<#(#generic_idents),*> | ||
where #(#generic_idents: __futures_crate::stream::Stream<Item=ITEM> + ::std::marker::Unpin,)* | ||
{ | ||
type Item = ITEM; | ||
|
||
fn poll_next(mut self: ::std::pin::Pin<&mut Self>, cx: &mut __futures_crate::task::Context<'_>) -> __futures_crate::task::Poll<Option<Self::Item>> { | ||
match self.get_mut() { | ||
#( | ||
Self::#generic_idents(#generic_idents) => ::std::pin::Pin::new(#generic_idents).poll_next(cx) | ||
),*, | ||
Self::None => panic!("StreamEnum::None should never be polled!"), | ||
} | ||
} | ||
} | ||
|
||
impl<ITEM, #(#generic_idents),*> __futures_crate::stream::Stream for StreamSelect<#(#generic_idents),*> | ||
where #(#generic_idents: __futures_crate::stream::Stream<Item=ITEM> + ::std::marker::Unpin,)* | ||
{ | ||
type Item = ITEM; | ||
|
||
fn poll_next(mut self: ::std::pin::Pin<&mut Self>, cx: &mut __futures_crate::task::Context<'_>) -> __futures_crate::task::Poll<Option<Self::Item>> { | ||
let Self(#(ref mut #field_idents),*) = self.get_mut(); | ||
#( | ||
let mut #field_idents_2 = false; | ||
)* | ||
let mut any_pending = false; | ||
{ | ||
let mut stream_array = [#(#field_idents.as_mut().map(|f| StreamEnum::#generic_idents(f)).unwrap_or(StreamEnum::None)),*]; | ||
__futures_crate::async_await::shuffle(&mut stream_array); | ||
|
||
for mut s in stream_array { | ||
if let StreamEnum::None = s { | ||
continue; | ||
} else { | ||
match __futures_crate::stream::Stream::poll_next(::std::pin::Pin::new(&mut s), cx) { | ||
r @ __futures_crate::task::Poll::Ready(Some(_)) => { | ||
return r; | ||
}, | ||
__futures_crate::task::Poll::Pending => { | ||
any_pending = true; | ||
}, | ||
__futures_crate::task::Poll::Ready(None) => { | ||
match s { | ||
#( | ||
StreamEnum::#generic_idents(_) => { #field_idents_2 = true; } | ||
),*, | ||
StreamEnum::None => panic!("StreamEnum::None should never be polled!"), | ||
} | ||
}, | ||
} | ||
} | ||
} | ||
} | ||
#( | ||
if #field_idents_2 { | ||
*#field_idents = None; | ||
} | ||
)* | ||
if any_pending { | ||
__futures_crate::task::Poll::Pending | ||
} else { | ||
__futures_crate::task::Poll::Ready(None) | ||
} | ||
} | ||
|
||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
let mut s = (0, Some(0)); | ||
#( | ||
if let Some(new_hint) = self.#field_indices.as_ref().map(|s| s.size_hint()) { | ||
s.0 += new_hint.0; | ||
// We can change this out for `.zip` when the MSRV is 1.46.0 or higher. | ||
s.1 = s.1.and_then(|a| new_hint.1.map(|b| a + b)); | ||
} | ||
)* | ||
s | ||
} | ||
} | ||
|
||
StreamSelect(#(Some(#args)),*) | ||
|
||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//! The `stream_select` macro. | ||
|
||
#[cfg(feature = "std")] | ||
#[allow(unreachable_pub)] | ||
#[doc(hidden)] | ||
#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] | ||
pub use futures_macro::stream_select_internal; | ||
|
||
/// Combines several streams, all producing the same `Item` type, into one stream. | ||
/// This is similar to `select_all` but does not require the streams to all be the same type. | ||
/// It also keeps the streams inline, and does not require `Box<dyn Stream>`s to be allocated. | ||
/// Streams passed to this macro must be `Unpin`. | ||
/// | ||
/// If multiple streams are ready, one will be pseudo randomly selected at runtime. | ||
/// | ||
/// This macro is gated behind the `async-await` feature of this library, which is activated by default. | ||
/// Note that `stream_select!` relies on `proc-macro-hack`, and may require to set the compiler's recursion | ||
/// limit very high, e.g. `#![recursion_limit="1024"]`. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// # futures::executor::block_on(async { | ||
/// use futures::{stream, StreamExt, stream_select}; | ||
/// let endless_ints = |i| stream::iter(vec![i].into_iter().cycle()).fuse(); | ||
/// | ||
/// let mut endless_numbers = stream_select!(endless_ints(1i32), endless_ints(2), endless_ints(3)); | ||
/// match endless_numbers.next().await { | ||
/// Some(1) => println!("Got a 1"), | ||
/// Some(2) => println!("Got a 2"), | ||
/// Some(3) => println!("Got a 3"), | ||
/// _ => unreachable!(), | ||
/// } | ||
/// # }); | ||
/// ``` | ||
#[cfg(feature = "std")] | ||
#[macro_export] | ||
macro_rules! stream_select { | ||
($($tokens:tt)*) => {{ | ||
use $crate::__private as __futures_crate; | ||
$crate::stream_select_internal! { | ||
$( $tokens )* | ||
} | ||
}} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters