Skip to main content

rustigram_bot/
handler.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use async_trait::async_trait;
6
7use crate::context::Context;
8use crate::error::BotResult;
9
10/// Type alias for the boxed future returned by handlers.
11pub type HandlerFuture = Pin<Box<dyn Future<Output = BotResult<()>> + Send>>;
12
13/// Core trait for all update handlers.
14///
15/// Implementors receive a [`Context`] and return a future that resolves to
16/// `BotResult<()>`. Returning `Ok(())` allows subsequent handlers to run;
17/// the dispatcher stops the chain on `Err`.
18#[async_trait]
19pub trait Handler: Send + Sync + 'static {
20    /// Processes an incoming update and returns whether the dispatcher should continue.
21    async fn handle(&self, ctx: Context) -> BotResult<()>;
22}
23
24/// A boxed, type-erased handler.
25pub type BoxHandler = Arc<dyn Handler>;
26
27/// Wraps an async function (or closure) as a [`Handler`].
28pub struct HandlerFn<F> {
29    f: F,
30}
31
32impl<F, Fut> HandlerFn<F>
33where
34    F: Fn(Context) -> Fut + Send + Sync + 'static,
35    Fut: Future<Output = BotResult<()>> + Send + 'static,
36{
37    /// Creates a new `HandlerFn` from a function or closure.
38    pub fn new(f: F) -> Self {
39        Self { f }
40    }
41}
42
43#[async_trait]
44impl<F, Fut> Handler for HandlerFn<F>
45where
46    F: Fn(Context) -> Fut + Send + Sync + 'static,
47    Fut: Future<Output = BotResult<()>> + Send + 'static,
48{
49    async fn handle(&self, ctx: Context) -> BotResult<()> {
50        (self.f)(ctx).await
51    }
52}
53
54/// Creates a [`BoxHandler`] from any async function or closure.
55///
56/// # Example
57///
58/// ```rust,ignore
59/// use rustigram_bot::{handler_fn, Context, BotResult};
60///
61/// let h = handler_fn(|ctx: Context| async move {
62///     if let Some(r) = ctx.reply("pong") { r.await?; }
63///     Ok(())
64/// });
65/// ```
66pub fn handler_fn<F, Fut>(f: F) -> BoxHandler
67where
68    F: Fn(Context) -> Fut + Send + Sync + 'static,
69    Fut: Future<Output = BotResult<()>> + Send + 'static,
70{
71    Arc::new(HandlerFn::new(f))
72}
73
74/// A handler that logs an error and continues the chain.
75pub struct LoggingHandler {
76    inner: BoxHandler,
77}
78
79impl LoggingHandler {
80    /// Wraps a handler in a logging layer that catches errors and logs them
81    /// instead of propagating them, allowing the dispatcher to continue.
82    pub fn wrap(inner: BoxHandler) -> BoxHandler {
83        Arc::new(Self { inner })
84    }
85}
86
87#[async_trait]
88impl Handler for LoggingHandler {
89    async fn handle(&self, ctx: Context) -> BotResult<()> {
90        let update_id = ctx.update_id();
91        if let Err(e) = self.inner.handle(ctx).await {
92            tracing::error!("Handler error on update {}: {}", update_id, e);
93        }
94        Ok(())
95    }
96}
97
98// Arc<dyn Handler> itself implements Handler so BoxHandler can be passed
99// directly to DispatcherBuilder::on() without unwrapping.
100#[async_trait]
101impl Handler for std::sync::Arc<dyn Handler> {
102    async fn handle(&self, ctx: Context) -> BotResult<()> {
103        (**self).handle(ctx).await
104    }
105}