use std::any::Any;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use rust_tg_bot_raw::types::update::Update;
use crate::context::CallbackContext;
#[derive(Debug)]
#[non_exhaustive]
pub enum MatchResult {
Empty,
Args(Vec<String>),
RegexMatch(Vec<String>),
RegexMatchWithNames {
positional: Vec<String>,
named: HashMap<String, String>,
},
Custom(Box<dyn Any + Send>),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum HandlerResult {
Continue,
Stop,
Error(Box<dyn std::error::Error + Send + Sync>),
}
pub type HandlerCallback = Arc<
dyn Fn(Arc<Update>, MatchResult) -> Pin<Box<dyn Future<Output = HandlerResult> + Send>>
+ Send
+ Sync,
>;
pub type ContextCallback = Arc<
dyn Fn(
Arc<Update>,
CallbackContext,
)
-> Pin<Box<dyn Future<Output = Result<(), crate::application::HandlerError>> + Send>>
+ Send
+ Sync,
>;
pub trait Handler: Send + Sync {
fn check_update(&self, update: &Update) -> Option<MatchResult>;
fn handle_update(
&self,
update: Arc<Update>,
match_result: MatchResult,
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send>>;
fn block(&self) -> bool {
true
}
fn collect_additional_context(
&self,
_context: &mut CallbackContext,
_match_result: &MatchResult,
) {
}
fn handle_update_with_context(
&self,
update: Arc<Update>,
match_result: MatchResult,
_context: CallbackContext,
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send>> {
self.handle_update(update, match_result)
}
}
pub struct FnHandler {
check: Arc<dyn Fn(&Update) -> bool + Send + Sync>,
context_callback: ContextCallback,
}
impl FnHandler {
pub fn new<C, Cb, Fut>(check: C, callback: Cb) -> Self
where
C: Fn(&Update) -> bool + Send + Sync + 'static,
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
let cb = Arc::new(callback);
let context_cb: ContextCallback = Arc::new(move |update, ctx| {
let fut = cb(update, ctx);
Box::pin(fut)
as Pin<
Box<dyn Future<Output = Result<(), crate::application::HandlerError>> + Send>,
>
});
Self {
check: Arc::new(check),
context_callback: context_cb,
}
}
pub fn on_callback_query<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.callback_query().is_some(), callback)
}
pub fn on_inline_query<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.inline_query().is_some(), callback)
}
pub fn on_poll<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.poll().is_some(), callback)
}
pub fn on_poll_answer<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.poll_answer().is_some(), callback)
}
pub fn on_shipping_query<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.shipping_query().is_some(), callback)
}
pub fn on_pre_checkout_query<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.pre_checkout_query().is_some(), callback)
}
pub fn on_chat_member<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.chat_member().is_some(), callback)
}
pub fn on_my_chat_member<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.my_chat_member().is_some(), callback)
}
pub fn on_message<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|u| u.message().is_some(), callback)
}
pub fn on_any<Cb, Fut>(callback: Cb) -> Self
where
Cb: Fn(Arc<Update>, CallbackContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), crate::application::HandlerError>> + Send + 'static,
{
Self::new(|_| true, callback)
}
}
impl Handler for FnHandler {
fn check_update(&self, update: &Update) -> Option<MatchResult> {
if (self.check)(update) {
Some(MatchResult::Empty)
} else {
None
}
}
fn handle_update(
&self,
_update: Arc<Update>,
_match_result: MatchResult,
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send>> {
Box::pin(async { HandlerResult::Continue })
}
fn handle_update_with_context(
&self,
update: Arc<Update>,
_match_result: MatchResult,
context: CallbackContext,
) -> Pin<Box<dyn Future<Output = HandlerResult> + Send>> {
let fut = (self.context_callback)(update, context);
Box::pin(async move {
match fut.await {
Ok(()) => HandlerResult::Continue,
Err(crate::application::HandlerError::HandlerStop { .. }) => HandlerResult::Stop,
Err(crate::application::HandlerError::Other(e)) => HandlerResult::Error(e),
}
})
}
}