async_monad 0.2.0

Asynchronous monad for rust
Documentation
use std::future::Future;
use std::pin::Pin;

use super::result::AsyncResult;

pub struct AsyncOption<T: 'static> {
    future: Pin<Box<dyn Future<Output = Option<T>>>>,
}

impl<T: 'static> From<Option<T>> for AsyncOption<T> {
    fn from(value: Option<T>) -> Self {
        Self {
            future: Box::pin(async move { value }),
        }
    }
}

impl<T> Future for AsyncOption<T> {
    type Output = Option<T>;

    fn poll(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        Future::poll(self.future.as_mut(), cx)
    }
}

impl<T> AsyncOption<T> {
    /// Create a new asynchronous option.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::runtime::Runtime;
    /// # use async_monad::async_wrapped::option::AsyncOption;
    ///
    /// let runtime = Runtime::new().unwrap();
    /// let result = runtime.block_on(async {
    ///     let res: AsyncOption<u8> = AsyncOption::new(Some(1));
    ///     res.await
    /// });
    /// println!("{:?}", result);
    /// ```
    #[inline]
    #[must_use]
    pub fn new(value: Option<T>) -> AsyncOption<T> {
        Self {
            future: Box::pin(async move { value }),
        }
    }

    /// Create a new asynchronous option from a raw future.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::runtime::Runtime;
    /// # use async_monad::async_wrapped::option::AsyncOption;
    ///
    /// let runtime = Runtime::new().unwrap();
    /// let result = runtime.block_on(async {
    ///     let res: AsyncOption<u8> = AsyncOption::raw(async { Some(1) });
    ///     res.await
    /// });
    /// println!("{:?}", result);
    /// ```
    #[inline]
    #[must_use]
    pub fn raw<V: Future<Output = Option<T>> + 'static>(value: V) -> AsyncOption<T> {
        Self {
            future: Box::pin(value),
        }
    }

    #[inline]
    #[must_use]
    pub fn and<U>(self, optb: impl Future<Output = Option<U>> + 'static) -> AsyncOption<U> {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(_) => optb.await,
                    None => None,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn and_then<F, V>(self, f: impl FnOnce(T) -> F + 'static) -> AsyncOption<V>
    where
        F: Future<Output = Option<V>> + 'static,
        V: 'static,
    {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(val) => f(val).await,
                    None => None,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn or(self, optb: impl Future<Output = Option<T>> + 'static) -> AsyncOption<T> {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(x) => Some(x),
                    None => optb.await,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn or_else<F>(self, f: impl FnOnce() -> F + 'static) -> AsyncOption<T>
    where
        F: Future<Output = Option<T>>,
    {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(x) => Some(x),
                    None => f().await,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn xor(self, optb: impl Future<Output = Option<T>> + 'static) -> AsyncOption<T> {
        AsyncOption {
            future: Box::pin(async move {
                if let Some(a) = self.future.await {
                    if optb.await.is_some() {
                        None
                    } else {
                        Some(a)
                    }
                } else {
                    optb.await
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn ok_or<E: 'static>(self, err: impl Future<Output = E> + 'static) -> AsyncResult<T, E> {
        AsyncResult::raw(async move { self.future.await.ok_or(err.await) })
    }

    #[inline]
    #[must_use]
    pub fn ok_or_else<E, F>(self, f: impl FnOnce() -> F + 'static) -> AsyncResult<T, E>
    where
        F: Future<Output = E> + 'static,
        E: 'static,
    {
        AsyncResult::raw(async move { self.future.await.ok_or(f().await) })
    }

    #[inline]
    #[must_use]
    pub fn map<F, V>(self, f: impl FnOnce(T) -> F + 'static) -> AsyncOption<V>
    where
        F: Future<Output = V> + 'static,
        V: 'static,
    {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(v) => Some(f(v).await),
                    None => None,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn zip<U>(self, other: impl Future<Output = Option<U>> + 'static) -> AsyncOption<(T, U)> {
        AsyncOption {
            future: Box::pin(async move {
                if let Some(a) = self.future.await {
                    if let Some(b) = other.await {
                        return Some((a, b));
                    }
                }
                None
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn zip_with<U, F, R>(
        self,
        other: impl Future<Output = Option<U>> + 'static,
        f: impl FnOnce(T, U) -> F + 'static,
    ) -> AsyncOption<R>
    where
        F: Future<Output = R>,
    {
        AsyncOption {
            future: Box::pin(async move {
                if let Some(a) = self.future.await {
                    if let Some(b) = other.await {
                        return Some(f(a, b).await);
                    }
                }
                None
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn filter<F>(self, predicate: impl FnOnce(&T) -> F + 'static) -> Self
    where
        F: Future<Output = bool>,
    {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(x) => {
                        if predicate(&x).await {
                            Some(x)
                        } else {
                            None
                        }
                    }
                    None => None,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub fn inspect<F>(self, f: impl FnOnce(&T) -> F + 'static) -> Self
    where
        F: Future<Output = ()> + 'static,
    {
        AsyncOption {
            future: Box::pin(async move {
                match self.future.await {
                    Some(v) => {
                        f(&v).await;
                        Some(v)
                    }
                    None => None,
                }
            }),
        }
    }

    #[inline]
    #[must_use]
    pub async fn map_or<U, F>(self, default: F, f: impl FnOnce(T) -> F) -> U
    where
        F: Future<Output = U> + 'static,
        U: 'static,
    {
        match self.future.await {
            Some(v) => f(v).await,
            None => default.await,
        }
    }

    #[inline]
    #[must_use]
    pub async fn map_or_else<U, F>(self, default: impl FnOnce() -> F, f: impl FnOnce(T) -> F) -> U
    where
        F: Future<Output = U> + 'static,
        U: 'static,
    {
        match self.future.await {
            Some(v) => f(v).await,
            None => default().await,
        }
    }

    #[inline]
    #[must_use]
    pub async fn is_none(self) -> bool {
        self.future.await.is_none()
    }

    #[inline]
    #[must_use]
    pub async fn is_some(self) -> bool {
        self.future.await.is_some()
    }

    #[inline]
    #[must_use]
    pub async fn is_some_and<F>(self, f: impl FnOnce(T) -> F) -> bool
    where
        F: Future<Output = bool>,
    {
        match self.future.await {
            Some(s) => f(s).await,
            None => false,
        }
    }

    #[inline]
    #[must_use]
    pub async fn is_none_or<F>(self, f: impl FnOnce(T) -> F) -> bool
    where
        F: Future<Output = bool>,
    {
        match self.future.await {
            Some(s) => f(s).await,
            None => true,
        }
    }
}

impl<T, E> AsyncOption<AsyncResult<T, E>> {
    /// This operation does not return an [`AsyncOption`] because it has to `await` both
    /// the result and the option, thus creating another [`AsyncOption`] is only an
    /// additional burden.
    pub fn transpose(self) -> AsyncResult<Option<T>, E> {
        AsyncResult::raw(async move {
            match self.future.await {
                Some(val) => match val.await {
                    Ok(x) => Ok(Some(x)),
                    Err(e) => Err(e),
                },
                None => Ok(None),
            }
        })
    }
}

impl<T, E> AsyncOption<Result<T, E>> {
    /// This operation does not return an [`AsyncOption`] because it has to `await`
    /// the option, thus creating another [`AsyncOption`] is only an additional burden.
    pub fn transpose(self) -> AsyncResult<Option<T>, E> {
        AsyncResult::raw(async move {
            match self.future.await {
                Some(val) => val.map(Some),
                None => Ok(None),
            }
        })
    }
}

impl<T> AsyncOption<AsyncOption<T>> {
    pub fn flatten(self) -> AsyncOption<T> {
        AsyncOption {
            future: Box::pin(async move {
                if let Some(val) = self.future.await {
                    if let Some(x) = val.await {
                        return Some(x);
                    }
                }
                None
            }),
        }
    }
}

impl<T> AsyncOption<Option<T>> {
    pub fn flatten(self) -> AsyncOption<T> {
        AsyncOption {
            future: Box::pin(async move { self.future.await? }),
        }
    }
}