telers 1.0.0-beta.7

An asynchronous framework for Telegram Bot API written in Rust
Documentation
use super::{And, Invert, Or};
use crate::{
    client::Reqwest,
    errors::FilterError,
    event::service::{service_fn, BoxCloneService},
    Request,
};

use std::future::Future;

pub type BoxedCloneFilterService<Client> =
    BoxCloneService<Request<Client>, (bool, Request<Client>), FilterError>;

#[allow(type_alias_bounds)]
pub type FilterResult<E: Into<anyhow::Error> = FilterError> = Result<bool, E>;

/// Filters are used to filter updates before processing handlers and inner middlewares.
/// You can use filters to check if the update meets the necessary conditions,
/// and if it does, the update will be processed by the handler(s) and/or inner middleware(s).
///
/// # Examples
///
/// A custom filter that passes only messages with a photo:
/// ```rust
/// use std::{convert::Infallible, future::Future};
/// use telers::{
///     filters::{Filter, FilterResult},
///     Request,
/// };
///
/// #[derive(Clone)]
/// struct HasPhoto;
///
/// impl<Client> Filter<Client> for HasPhoto
/// where
///     Client: Send,
/// {
///     type Error = Infallible;
///
///     fn check(
///         &mut self,
///         request: &mut Request<Client>,
///     ) -> impl Future<Output = FilterResult<Self::Error>> {
///         let res = request
///             .update
///             .message()
///             .is_some_and(|message| message.photo().is_some());
///         async move { Ok(res) }
///     }
/// }
/// ```
/// # Notes
/// Check out the examples to see how to create your own filters and check ready-made implementations of filters
/// to avoid writing your own filters which are already implemented.
pub trait Filter<Client = Reqwest>: Clone + Send + Sync + 'static {
    /// Error type returned by [`Filter::check`]; use [`Infallible`](std::convert::Infallible)
    /// if the filter can't fail
    type Error: Into<anyhow::Error>;

    /// Check if the filter passes
    /// # Returns
    /// `true` if the filter passes, otherwise `false`
    fn check(
        &mut self,
        request: &mut Request<Client>,
    ) -> impl Future<Output = FilterResult<Self::Error>> + Send;

    /// Invert result of the filter
    /// # Notes
    /// This method is used to create [`Invert`] filter
    fn invert(self) -> impl Filter<Client>
    where
        Client: Send,
    {
        Invert(self)
    }

    /// Combine two filters with logical `and`
    /// # Notes
    /// This method is used to create [`And`] filter
    fn and<F>(self, filter: F) -> impl Filter<Client>
    where
        F: Filter<Client>,
        Client: Send,
    {
        And(self, filter)
    }

    /// Combine two filters with logical `or`
    /// # Notes
    /// This method is used to create [`Or`] filter
    fn or<F>(self, filter: F) -> impl Filter<Client>
    where
        F: Filter<Client>,
        Client: Send,
    {
        Or(self, filter)
    }
}

impl<Client, F, Fut, Err> Filter<Client> for F
where
    Client: Send + Sync + 'static,
    F: FnMut(&mut Request<Client>) -> Fut + Clone + Send + Sync + 'static,
    Err: Into<anyhow::Error>,
    Fut: Future<Output = FilterResult<Err>> + Send,
{
    type Error = Err;

    fn check(
        &mut self,
        request: &mut Request<Client>,
    ) -> impl Future<Output = FilterResult<Self::Error>> + Send {
        self(request)
    }
}

pub fn boxed_filter_factory<Client, F>(filter: F) -> BoxedCloneFilterService<Client>
where
    Client: Send + Sync + 'static,
    F: Filter<Client>,
{
    BoxCloneService::new(service_fn(move |mut request| {
        let mut filter = filter.clone();

        async move {
            match filter.check(&mut request).await {
                Ok(result) => Ok((result, request)),
                Err(err) => Err(FilterError::new(err)),
            }
        }
    }))
}