use pin_project_lite::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
struct SelectFuture<'a, F: Future> {
#[pin] futures: &'a mut Vec<F> ,
}
}
impl<'a, F: Future> SelectFuture<'a, F> {
#[inline]
fn new(futures: &'a mut Vec<F>) -> Self {
Self { futures }
}
}
impl<'a, F: Future> Future for SelectFuture<'a, F> {
type Output = F::Output;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut ready = None;
let mut this = self.project();
for (i, f) in this.futures.iter_mut().enumerate() {
let p = unsafe { Pin::new_unchecked(f) };
if let Poll::Ready(output) = p.poll(cx) {
ready = Some((i, Poll::Ready(output)));
break;
}
}
if let Some((id, r)) = ready {
this.futures.swap_remove(id);
return r;
}
Poll::Pending
}
}
pub struct SelectAll<F: Future> {
futures: Vec<F>,
}
impl<I> From<I> for SelectAll<I::Item>
where
I: IntoIterator,
I::Item: Future,
{
#[inline]
fn from(iter: I) -> Self {
Self {
futures: iter.into_iter().collect(),
}
}
}
impl<F: Future> SelectAll<F> {
#[allow(clippy::new_without_default)]
#[inline]
pub fn new() -> Self {
SelectAll {
futures: Vec::new(),
}
}
#[inline]
pub fn len(&self) -> usize {
self.futures.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.futures.is_empty()
}
#[inline]
pub fn push(&mut self, future: F) {
self.futures.push(future);
}
#[inline]
pub async fn select(&mut self) -> F::Output {
assert!(!self.futures.is_empty());
SelectFuture::new(&mut self.futures).await
}
}