ufotofu 0.12.5

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

/// A wrapper for a [`Consumer`] which tracks the [`Status`] of the wrapped consumer, and the number of [`items`](Consumer::Item) consumed since the wrapper was created.
///
/// Use the [`into_inner`](Stats::into_inner) method to consume the wrapper and access the wrapped consumer.
///
/// Created via the [`ConsumerExt::to_stats`] method.
///
/// <br/>Counterpart: the [`producer::Stats`] type.
pub struct Stats<C> {
    inner: C,
    count: usize,
    status: Status,
}

impl<C> Stats<C> {
    pub(crate) fn new(inner: C) -> Self {
        Stats {
            inner,
            count: 0,
            status: Status::Processing,
        }
    }

    /// Returns the number of [`items`](Consumer::Item) which have been consumed since since this wrapper was created.
    pub fn count(&self) -> usize {
        self.count
    }

    /// Returns the [`Status`] of the [`Consumer`].
    pub fn status(&self) -> Status {
        self.status
    }

    /// Consumes this wrapper to return the inner [`Consumer`].
    pub fn into_inner(self) -> C {
        self.inner
    }
}

impl<C> Consumer for Stats<C>
where
    C: Consumer,
{
    type Item = C::Item;

    type Final = C::Final;

    type Error = C::Error;

    async fn consume(&mut self, val: Either<Self::Item, Self::Final>) -> Result<(), Self::Error> {
        let finalised = val.is_right();

        let result = self.inner.consume(val).await;

        match result {
            Ok(()) => {
                if finalised {
                    self.status = Status::Finalised;
                } else {
                    self.count = self
                        .count
                        .checked_add(1)
                        .expect("consumer stats cannot count more than usize::MAX items");
                }
            }
            Err(_) => {
                self.status = Status::Errored;
            }
        }
        result
    }

    async fn flush(&mut self) -> Result<(), Self::Error> {
        let result = self.inner.flush().await;
        if result.is_err() {
            self.status = Status::Errored;
        }
        result
    }
}

impl<C> BulkConsumer for Stats<C>
where
    C: BulkConsumer,
{
    async fn expose_slots_gracefully<F, R>(&mut self, f: F) -> Result<R, (F, Self::Error)>
    where
        F: AsyncFnOnce(&mut [Self::Item]) -> (usize, R),
    {
        let mut f = Some(f);

        let result = match self
            .inner
            .expose_slots_gracefully(async |items| {
                let f = f.take().expect("constructed as a Some variant");
                let (consumed, result) = f(items).await;
                self.count = self
                    .count
                    .checked_add(consumed)
                    .expect("consumer stats cannot count more than usize::MAX items");
                (consumed, result)
            })
            .await
        {
            Ok(result) => Ok(result),
            Err((_, err)) => {
                self.status = Status::Errored;
                Err((
                    f.take()
                        .expect("provided closure must not be called when an error occurs"),
                    err,
                ))
            }
        };
        result
    }
}