slice_ops 1.0.7

A selection of useful slice operations.
Documentation
use core::{alloc::Allocator, future::Future, pin::Pin, task::{Context, Poll}};
use alloc::{alloc::Global, boxed::Box};

use crate::private;

use super::MaybeDone;

#[cfg(feature = "alloc")]
pub struct TryBoxedActions<T, E, A = Global>
where
    T: Future<Output = Result<(), E>>,
    A: Allocator
{
    tasks: Box<[MaybeDone<T>], A>
}
#[cfg(not(feature = "alloc"))]
pub struct TryBoxedActions<T, E, A>
where
    T: Future<Output = Result<(), E>>,
    A: Allocator
{
    tasks: Box<[T], A>
}

#[cfg(feature = "alloc")]
impl<T, E> TryBoxedActions<T, E>
where
    T: Future<Output = Result<(), E>>
{
    pub(crate) fn new<I>(tasks: I) -> Self
    where
        I: ExactSizeIterator<Item = T>
    {
        Self::new_in(tasks, Global)
    }
}

impl<T, E, A> TryBoxedActions<T, E, A>
where
    T: Future<Output = Result<(), E>>,
    A: Allocator
{
    pub(crate) fn new_in<I>(tasks: I, alloc: A) -> Self
    where
        I: ExactSizeIterator<Item = T>
    {
        Self {
            tasks: private::collect_boxed_slice_in(tasks.map(|task| MaybeDone::Future(task)), alloc)
        }
    }
}

impl<T, E, A> Future for TryBoxedActions<T, E, A>
where
    T: Future<Output = Result<(), E>>,
    A: Allocator
{
    type Output = Result<(), E>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>
    {
        let mut done = true;

        let len = self.tasks.len();
        let mut i = 0;
        
        while i < len
        {
            let task = unsafe {
                self.as_mut()
                    .map_unchecked_mut(|join| &mut join.tasks[i])
            };
            if !task.is_taken()
            {
                let ready = task.poll(cx)
                    .is_ready();
                if ready
                {
                    let join = unsafe {
                        self.as_mut()
                            .get_unchecked_mut()
                    };
                    let result = join.tasks[i].take_output();
                    if let Some(result) = result && result.is_err()
                    {
                        for task in join.tasks.iter_mut()
                        {
                            task.cancel()
                        }
                        return Poll::Ready(result)
                    }
                }
                else
                {
                    done = false
                }
            }
            i += 1;
        }

        if !done
        {
            return Poll::Pending
        }

        Poll::Ready(Ok(()))
    }
}