ufotofu 0.12.5

Abstractions for lazily consuming and producing sequences
Documentation
use crate::prelude::*;

/// A wrapper for a pair of [`Producer`]s, which first produces all the [`items`](Producer::Item) of the first producer, then all items of the second producer, then the [`final`](Producer::Final) value of the second producer.
///
/// Created via [`ProducerExt::to_chain`].
///
/// <br/>Counterpart: the [`consumer::Chain`] type.
#[derive(Debug)]
pub struct Chain<P, Q> {
    chained: (P, Q),
    first_exhausted: bool,
}

impl<P, Q> Chain<P, Q> {
    pub(crate) fn new(first: P, second: Q) -> Self {
        Self {
            chained: (first, second),
            first_exhausted: false,
        }
    }

    /// Consumes `self` to return its two wrapped [`producer`](crate::producer::Producer)s.
    pub fn into_inner(self) -> (P, Q) {
        self.chained
    }

    /// Returns `true` if the first [`Producer`] in the chain has produced all its [`items`](Producer::Item), else `false`.
    ///
    /// If this method returns `true`, no further [`Producer`] trait methods may be called on the first producer in the chain.
    pub fn first_exhausted(&self) -> bool {
        self.first_exhausted
    }
}

impl<P, Q> AsRef<(P, Q)> for Chain<P, Q> {
    fn as_ref(&self) -> &(P, Q) {
        &self.chained
    }
}

impl<P, Q> Producer for Chain<P, Q>
where
    P: Producer<Final = ()>,
    Q: Producer<Item = P::Item>,
{
    type Item = P::Item;

    type Final = Q::Final;

    type Error = Either<P::Error, Q::Error>;

    async fn produce(&mut self) -> Result<Either<Self::Item, Self::Final>, Self::Error> {
        if !self.first_exhausted {
            match self.chained.0.produce().await.map_err(Left)? {
                Left(item) => return Ok(Left(item)),
                Right(_) => self.first_exhausted = true,
            }
        }

        self.chained.1.produce().await.map_err(Right)
    }

    async fn slurp(&mut self) -> Result<(), Self::Error> {
        if !self.first_exhausted {
            self.chained.0.slurp().await.map_err(Left)?;
        } else {
            self.chained.1.slurp().await.map_err(Right)?
        }

        Ok(())
    }
}

impl<P, Q> BulkProducer for Chain<P, Q>
where
    P: BulkProducer<Final = ()>,
    Q: BulkProducer<Item = P::Item>,
{
    async fn expose_items_gracefully<F, R>(
        &mut self,
        f: F,
    ) -> Result<Either<R, (F, Self::Final)>, (F, Self::Error)>
    where
        F: AsyncFnOnce(&[Self::Item]) -> (usize, R),
    {
        if self.first_exhausted {
            return self
                .chained
                .1
                .expose_items_gracefully(f)
                .await
                .map_err(|(f, err)| (f, Right(err)));
        }

        match self.chained.0.expose_items_gracefully(f).await {
            Ok(Left(yay)) => Ok(Left(yay)),
            Err((f, err)) => Err((f, Left(err))),
            Ok(Right((f, ()))) => {
                self.first_exhausted = true;
                self.chained
                    .1
                    .expose_items_gracefully(f)
                    .await
                    .map_err(|(f, err)| (f, Right(err)))
            }
        }
    }
}