mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Divide AsyncHandler into different traits
This commit is contained in:
parent
ee3a95b31e
commit
7e34007a4d
10 changed files with 306 additions and 213 deletions
|
@ -1,145 +0,0 @@
|
|||
use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};
|
||||
|
||||
/// An asynchronous polymorphic handler of a context.
|
||||
///
|
||||
/// See [the module-level documentation for the design
|
||||
/// overview](crate::dispatching).
|
||||
pub trait AsyncHandler<Ctx, Output> {
|
||||
#[must_use]
|
||||
fn handle<'a>(
|
||||
&'a self,
|
||||
ctx: Ctx,
|
||||
) -> Pin<Box<dyn Future<Output = Output> + 'a>>
|
||||
where
|
||||
Ctx: 'a;
|
||||
}
|
||||
|
||||
impl<Ctx, Output, F, Fut> AsyncHandler<Ctx, Output> for F
|
||||
where
|
||||
F: Fn(Ctx) -> Fut,
|
||||
Fut: Future<Output = Output>,
|
||||
{
|
||||
fn handle<'a>(
|
||||
&'a self,
|
||||
ctx: Ctx,
|
||||
) -> Pin<Box<dyn Future<Output = Fut::Output> + 'a>>
|
||||
where
|
||||
Ctx: 'a,
|
||||
{
|
||||
Box::pin(async move { self(ctx).await })
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler that silently ignores all values.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main_() {
|
||||
/// use teloxide::dispatching::{AsyncHandler, IgnoringHandler};
|
||||
///
|
||||
/// IgnoringHandler.handle(()).await;
|
||||
/// IgnoringHandler.handle(404).await;
|
||||
/// IgnoringHandler.handle("error").await;
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct IgnoringHandler;
|
||||
|
||||
impl<Ctx> AsyncHandler<Ctx, ()> for IgnoringHandler {
|
||||
fn handle<'a>(&'a self, _: Ctx) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
Ctx: 'a,
|
||||
{
|
||||
Box::pin(async {})
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler that silently ignores all values that can never happen (e.g.:
|
||||
/// [`!`] or [`Infallible`]).
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main_() {
|
||||
/// use std::convert::{Infallible, TryInto};
|
||||
///
|
||||
/// use teloxide::dispatching::{AsyncHandler, IgnoringHandlerSafe};
|
||||
///
|
||||
/// let result: Result<String, Infallible> = "str".try_into();
|
||||
/// match result {
|
||||
/// Ok(string) => println!("{}", string),
|
||||
/// Err(inf) => IgnoringHandlerSafe.handle(inf).await,
|
||||
/// }
|
||||
///
|
||||
/// IgnoringHandlerSafe.handle(return).await; // return type of `return` is `!` (aka never)
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// use teloxide::dispatching::{AsyncHandler, IgnoringHandlerSafe};
|
||||
///
|
||||
/// IgnoringHandlerSafe.handle(0);
|
||||
/// ```
|
||||
///
|
||||
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
|
||||
/// [`Infallible`]: std::convert::Infallible
|
||||
pub struct IgnoringHandlerSafe;
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
impl AsyncHandler<Infallible, ()> for IgnoringHandlerSafe {
|
||||
fn handle<'a>(
|
||||
&'a self,
|
||||
_: Infallible,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
Infallible: 'a,
|
||||
{
|
||||
Box::pin(async {})
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler that log all values passed into it.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main_() {
|
||||
/// use teloxide::dispatching::{AsyncHandler, LoggingHandler};
|
||||
///
|
||||
/// LoggingHandler::default().handle(()).await;
|
||||
/// LoggingHandler::new("error").handle(404).await;
|
||||
/// LoggingHandler::new("error")
|
||||
/// .handle("Invalid data type!")
|
||||
/// .await;
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct LoggingHandler {
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl LoggingHandler {
|
||||
/// Creates `LoggingHandler` with a meta text before a log.
|
||||
///
|
||||
/// The logs will be printed in this format: `{text}: {:?}`.
|
||||
#[must_use]
|
||||
pub fn new<T>(text: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self { text: text.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx> AsyncHandler<Ctx, ()> for LoggingHandler
|
||||
where
|
||||
Ctx: Debug,
|
||||
{
|
||||
fn handle<'a>(&'a self, ctx: Ctx) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
Ctx: 'a,
|
||||
{
|
||||
log::debug!("{text}: {:?}", ctx, text = self.text);
|
||||
Box::pin(async {})
|
||||
}
|
||||
}
|
31
src/dispatching/ctx_handlers.rs
Normal file
31
src/dispatching/ctx_handlers.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use std::{future::Future, pin::Pin};
|
||||
|
||||
/// An asynchronous handler of a context.
|
||||
///
|
||||
/// See [the module-level documentation for the design
|
||||
/// overview](crate::dispatching).
|
||||
pub trait CtxHandler<Ctx, Output> {
|
||||
#[must_use]
|
||||
fn handle_ctx<'a>(
|
||||
&'a self,
|
||||
ctx: Ctx,
|
||||
) -> Pin<Box<dyn Future<Output = Output> + 'a>>
|
||||
where
|
||||
Ctx: 'a;
|
||||
}
|
||||
|
||||
impl<Ctx, Output, F, Fut> CtxHandler<Ctx, Output> for F
|
||||
where
|
||||
F: Fn(Ctx) -> Fut,
|
||||
Fut: Future<Output = Output>,
|
||||
{
|
||||
fn handle_ctx<'a>(
|
||||
&'a self,
|
||||
ctx: Ctx,
|
||||
) -> Pin<Box<dyn Future<Output = Fut::Output> + 'a>>
|
||||
where
|
||||
Ctx: 'a,
|
||||
{
|
||||
Box::pin(async move { self(ctx).await })
|
||||
}
|
||||
}
|
|
@ -1,19 +1,21 @@
|
|||
use crate::{
|
||||
dispatching::{
|
||||
update_listeners, update_listeners::UpdateListener, AsyncHandler,
|
||||
HandlerCtx, LoggingHandler,
|
||||
error_handlers::ErrorHandler, update_listeners,
|
||||
update_listeners::UpdateListener, CtxHandler, DispatcherHandlerCtx,
|
||||
LoggingErrorHandler, Middleware,
|
||||
},
|
||||
types::{
|
||||
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||
PreCheckoutQuery, ShippingQuery, UpdateKind,
|
||||
PreCheckoutQuery, ShippingQuery, Update, UpdateKind,
|
||||
},
|
||||
Bot,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use futures::{stream, StreamExt};
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
|
||||
type H<'a, Upd, HandlerE> =
|
||||
Option<Box<dyn AsyncHandler<HandlerCtx<Upd>, Result<(), HandlerE>> + 'a>>;
|
||||
type H<'a, Upd, HandlerE> = Option<
|
||||
Box<dyn CtxHandler<DispatcherHandlerCtx<Upd>, Result<(), HandlerE>> + 'a>,
|
||||
>;
|
||||
|
||||
/// One dispatcher to rule them all.
|
||||
///
|
||||
|
@ -22,7 +24,9 @@ type H<'a, Upd, HandlerE> =
|
|||
pub struct Dispatcher<'a, HandlerE> {
|
||||
bot: Arc<Bot>,
|
||||
|
||||
handlers_error_handler: Box<dyn AsyncHandler<HandlerE, ()> + 'a>,
|
||||
middlewares: Vec<Box<dyn Middleware<Update> + 'a>>,
|
||||
|
||||
handlers_error_handler: Box<dyn ErrorHandler<HandlerE> + 'a>,
|
||||
|
||||
message_handler: H<'a, Message, HandlerE>,
|
||||
edited_message_handler: H<'a, Message, HandlerE>,
|
||||
|
@ -45,7 +49,8 @@ where
|
|||
pub fn new(bot: Bot) -> Self {
|
||||
Self {
|
||||
bot: Arc::new(bot),
|
||||
handlers_error_handler: Box::new(LoggingHandler::new(
|
||||
middlewares: Vec::new(),
|
||||
handlers_error_handler: Box::new(LoggingErrorHandler::new(
|
||||
"An error from a Dispatcher's handler",
|
||||
)),
|
||||
message_handler: None,
|
||||
|
@ -61,11 +66,21 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Appends a middleware.
|
||||
#[must_use]
|
||||
pub fn middleware<M>(mut self, val: M) -> Self
|
||||
where
|
||||
M: Middleware<Update> + 'a,
|
||||
{
|
||||
self.middlewares.push(Box::new(val));
|
||||
self
|
||||
}
|
||||
|
||||
/// Registers a handler of errors, produced by other handlers.
|
||||
#[must_use]
|
||||
pub fn handlers_error_handler<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: AsyncHandler<HandlerE, ()> + 'a,
|
||||
T: ErrorHandler<HandlerE> + 'a,
|
||||
{
|
||||
self.handlers_error_handler = Box::new(val);
|
||||
self
|
||||
|
@ -74,7 +89,7 @@ where
|
|||
#[must_use]
|
||||
pub fn message_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
{
|
||||
self.message_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -83,7 +98,7 @@ where
|
|||
#[must_use]
|
||||
pub fn edited_message_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
{
|
||||
self.edited_message_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -92,7 +107,7 @@ where
|
|||
#[must_use]
|
||||
pub fn channel_post_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
{
|
||||
self.channel_post_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -101,7 +116,7 @@ where
|
|||
#[must_use]
|
||||
pub fn edited_channel_post_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||
{
|
||||
self.edited_channel_post_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -110,7 +125,8 @@ where
|
|||
#[must_use]
|
||||
pub fn inline_query_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<InlineQuery>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<DispatcherHandlerCtx<InlineQuery>, Result<(), HandlerE>>
|
||||
+ 'a,
|
||||
{
|
||||
self.inline_query_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -119,8 +135,10 @@ where
|
|||
#[must_use]
|
||||
pub fn chosen_inline_result_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<ChosenInlineResult>, Result<(), HandlerE>>
|
||||
+ 'a,
|
||||
H: CtxHandler<
|
||||
DispatcherHandlerCtx<ChosenInlineResult>,
|
||||
Result<(), HandlerE>,
|
||||
> + 'a,
|
||||
{
|
||||
self.chosen_inline_result_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -129,7 +147,10 @@ where
|
|||
#[must_use]
|
||||
pub fn callback_query_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<CallbackQuery>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<
|
||||
DispatcherHandlerCtx<CallbackQuery>,
|
||||
Result<(), HandlerE>,
|
||||
> + 'a,
|
||||
{
|
||||
self.callback_query_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -138,7 +159,10 @@ where
|
|||
#[must_use]
|
||||
pub fn shipping_query_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<ShippingQuery>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<
|
||||
DispatcherHandlerCtx<ShippingQuery>,
|
||||
Result<(), HandlerE>,
|
||||
> + 'a,
|
||||
{
|
||||
self.shipping_query_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -147,8 +171,10 @@ where
|
|||
#[must_use]
|
||||
pub fn pre_checkout_query_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<PreCheckoutQuery>, Result<(), HandlerE>>
|
||||
+ 'a,
|
||||
H: CtxHandler<
|
||||
DispatcherHandlerCtx<PreCheckoutQuery>,
|
||||
Result<(), HandlerE>,
|
||||
> + 'a,
|
||||
{
|
||||
self.pre_checkout_query_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -157,7 +183,7 @@ where
|
|||
#[must_use]
|
||||
pub fn poll_handler<H>(mut self, h: H) -> Self
|
||||
where
|
||||
H: AsyncHandler<HandlerCtx<Poll>, Result<(), HandlerE>> + 'a,
|
||||
H: CtxHandler<DispatcherHandlerCtx<Poll>, Result<(), HandlerE>> + 'a,
|
||||
{
|
||||
self.poll_handler = Some(Box::new(h));
|
||||
self
|
||||
|
@ -170,7 +196,7 @@ where
|
|||
pub async fn dispatch(&'a self) {
|
||||
self.dispatch_with_listener(
|
||||
update_listeners::polling_default(Arc::clone(&self.bot)),
|
||||
&LoggingHandler::new("An error from the update listener"),
|
||||
&LoggingErrorHandler::new("An error from the update listener"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
@ -183,7 +209,7 @@ where
|
|||
update_listener_error_handler: &'a Eh,
|
||||
) where
|
||||
UListener: UpdateListener<ListenerE> + 'a,
|
||||
Eh: AsyncHandler<ListenerE, ()> + 'a,
|
||||
Eh: ErrorHandler<ListenerE> + 'a,
|
||||
ListenerE: Debug,
|
||||
{
|
||||
let update_listener = Box::pin(update_listener);
|
||||
|
@ -193,11 +219,17 @@ where
|
|||
let update = match update {
|
||||
Ok(update) => update,
|
||||
Err(error) => {
|
||||
update_listener_error_handler.handle(error).await;
|
||||
update_listener_error_handler.handle_error(error).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let update = stream::iter(&self.middlewares)
|
||||
.fold(update, |acc, middleware| async move {
|
||||
middleware.handle(acc).await
|
||||
})
|
||||
.await;
|
||||
|
||||
match update.kind {
|
||||
UpdateKind::Message(message) => {
|
||||
self.handle(&self.message_handler, message).await
|
||||
|
@ -241,13 +273,13 @@ where
|
|||
async fn handle<Upd>(&self, handler: &H<'a, Upd, HandlerE>, update: Upd) {
|
||||
if let Some(handler) = &handler {
|
||||
if let Err(error) = handler
|
||||
.handle(HandlerCtx {
|
||||
.handle_ctx(DispatcherHandlerCtx {
|
||||
bot: Arc::clone(&self.bot),
|
||||
update,
|
||||
})
|
||||
.await
|
||||
{
|
||||
self.handlers_error_handler.handle(error).await;
|
||||
self.handlers_error_handler.handle_error(error).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@ use std::sync::Arc;
|
|||
/// overview](crate::dispatching).
|
||||
///
|
||||
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
pub struct HandlerCtx<Upd> {
|
||||
pub struct DispatcherHandlerCtx<Upd> {
|
||||
pub bot: Arc<Bot>,
|
||||
pub update: Upd,
|
||||
}
|
||||
|
||||
impl<Upd> GetChatId for HandlerCtx<Upd>
|
||||
impl<Upd> GetChatId for DispatcherHandlerCtx<Upd>
|
||||
where
|
||||
Upd: GetChatId,
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl HandlerCtx<Message> {
|
||||
impl DispatcherHandlerCtx<Message> {
|
||||
pub async fn reply<T>(&self, text: T) -> ResponseResult<()>
|
||||
where
|
||||
T: Into<String>,
|
151
src/dispatching/error_handlers.rs
Normal file
151
src/dispatching/error_handlers.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};
|
||||
|
||||
/// An asynchronous handler of an error.
|
||||
///
|
||||
/// See [the module-level documentation for the design
|
||||
/// overview](crate::dispatching).
|
||||
pub trait ErrorHandler<E> {
|
||||
#[must_use]
|
||||
fn handle_error<'a>(
|
||||
&'a self,
|
||||
error: E,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
E: 'a;
|
||||
}
|
||||
|
||||
impl<E, F, Fut> ErrorHandler<E> for F
|
||||
where
|
||||
F: Fn(E) -> Fut,
|
||||
Fut: Future<Output = ()>,
|
||||
{
|
||||
fn handle_error<'a>(
|
||||
&'a self,
|
||||
error: E,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
E: 'a,
|
||||
{
|
||||
Box::pin(async move { self(error).await })
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler that silently ignores all errors.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main_() {
|
||||
/// use teloxide::dispatching::{ErrorHandler, IgnoringErrorHandler};
|
||||
///
|
||||
/// IgnoringErrorHandler.handle_error(()).await;
|
||||
/// IgnoringErrorHandler.handle_error(404).await;
|
||||
/// IgnoringErrorHandler.handle_error("error").await;
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct IgnoringErrorHandler;
|
||||
|
||||
impl<E> ErrorHandler<E> for IgnoringErrorHandler {
|
||||
fn handle_error<'a>(
|
||||
&'a self,
|
||||
_: E,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
E: 'a,
|
||||
{
|
||||
Box::pin(async {})
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler that silently ignores all errors that can never happen (e.g.:
|
||||
/// [`!`] or [`Infallible`]).
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main_() {
|
||||
/// use std::convert::{Infallible, TryInto};
|
||||
///
|
||||
/// use teloxide::dispatching::{ErrorHandler, IgnoringErrorHandlerSafe};
|
||||
///
|
||||
/// let result: Result<String, Infallible> = "str".try_into();
|
||||
/// match result {
|
||||
/// Ok(string) => println!("{}", string),
|
||||
/// Err(inf) => IgnoringErrorHandlerSafe.handle_error(inf).await,
|
||||
/// }
|
||||
///
|
||||
/// IgnoringErrorHandlerSafe.handle_error(return).await; // return type of `return` is `!` (aka never)
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// use teloxide::dispatching::{ErrorHandler, IgnoringErrorHandlerSafe};
|
||||
///
|
||||
/// IgnoringErrorHandlerSafe.handle_error(0);
|
||||
/// ```
|
||||
///
|
||||
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
|
||||
/// [`Infallible`]: std::convert::Infallible
|
||||
pub struct IgnoringErrorHandlerSafe;
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
impl ErrorHandler<Infallible> for IgnoringErrorHandlerSafe {
|
||||
fn handle_error<'a>(
|
||||
&'a self,
|
||||
_: Infallible,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
Infallible: 'a,
|
||||
{
|
||||
Box::pin(async {})
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler that log all errors passed into it.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main_() {
|
||||
/// use teloxide::dispatching::{ErrorHandler, LoggingErrorHandler};
|
||||
///
|
||||
/// LoggingErrorHandler::default().handle_error(()).await;
|
||||
/// LoggingErrorHandler::new("error").handle_error(404).await;
|
||||
/// LoggingErrorHandler::new("error")
|
||||
/// .handle_error("Invalid data type!")
|
||||
/// .await;
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct LoggingErrorHandler {
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl LoggingErrorHandler {
|
||||
/// Creates `LoggingErrorHandler` with a meta text before a log.
|
||||
///
|
||||
/// The logs will be printed in this format: `{text}: {:?}`.
|
||||
#[must_use]
|
||||
pub fn new<T>(text: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self { text: text.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> ErrorHandler<E> for LoggingErrorHandler
|
||||
where
|
||||
E: Debug,
|
||||
{
|
||||
fn handle_error<'a>(
|
||||
&'a self,
|
||||
error: E,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
||||
where
|
||||
E: 'a,
|
||||
{
|
||||
log::debug!("{text}: {:?}", error, text = self.text);
|
||||
Box::pin(async {})
|
||||
}
|
||||
}
|
25
src/dispatching/middleware.rs
Normal file
25
src/dispatching/middleware.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use std::{future::Future, pin::Pin};
|
||||
|
||||
/// An asynchronous middleware.
|
||||
///
|
||||
/// See [the module-level documentation for the design
|
||||
/// overview](crate::dispatching).
|
||||
pub trait Middleware<T> {
|
||||
#[must_use]
|
||||
fn handle<'a>(&'a self, val: T) -> Pin<Box<dyn Future<Output = T> + 'a>>
|
||||
where
|
||||
T: 'a;
|
||||
}
|
||||
|
||||
impl<T, F, Fut> Middleware<T> for F
|
||||
where
|
||||
F: Fn(T) -> Fut,
|
||||
Fut: Future<Output = T>,
|
||||
{
|
||||
fn handle<'a>(&'a self, val: T) -> Pin<Box<dyn Future<Output = T> + 'a>>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
Box::pin(async move { self(val).await })
|
||||
}
|
||||
}
|
|
@ -1,20 +1,19 @@
|
|||
//! Updates dispatching.
|
||||
//!
|
||||
//! The key type here is [`Dispatcher`]. It encapsulates [`UpdateListener`], a
|
||||
//! handler of errors, and handlers for [10 update kinds]. When [`Update`] is
|
||||
//! received from Telegram, it is supplied to an appropriate handler, and if a
|
||||
//! handler has returned an error, the error is supplied into an error handler.
|
||||
//! The key type here is [`Dispatcher`]. It encapsulates middlewares, handlers
|
||||
//! for [10 update kinds], and [`ErrorHandler`] for them. When [`Update`] is
|
||||
//! received from Telegram, the following steps are executed:
|
||||
//!
|
||||
//! 1. It is supplied into all registered middlewares.
|
||||
//! 2. It is supplied to an appropriate handler.
|
||||
//! 3. If a handler has returned an error, the error is supplied into an error
|
||||
//! handler.
|
||||
//!
|
||||
//! That's simple!
|
||||
//!
|
||||
//! All the handlers are of type [`AsyncHandler`]. It's like a [first-class
|
||||
//! construction] in this module, because:
|
||||
//! 1. It is implemented for [`SessionDispatcher`], which itself accepts
|
||||
//! [`AsyncHandler`].
|
||||
//! 2. It is implemented for [`LoggingHandler`], [`IgnoringHandler`], and
|
||||
//! [`IgnoringHandlerSafe`].
|
||||
//! 3. It is implemented even [for asynchronous functions].
|
||||
//! 4. You can use [`AsyncHandler`]s as error handlers.
|
||||
//! 5. More...
|
||||
//! Note that handlers implement [`CtxHandler`], which means that you are able
|
||||
//! to supply [`SessionDispatcher`] as a handler, since it implements
|
||||
//! [`CtxHandler`] too!
|
||||
//!
|
||||
//! ## Examples
|
||||
//! The ping-pong bot ([full](https://github.com/teloxide/teloxide/blob/dev/examples/ping_pong_bot/)):
|
||||
|
@ -27,7 +26,7 @@
|
|||
//! // Setup logging here...
|
||||
//!
|
||||
//! Dispatcher::new(Bot::new("MyAwesomeToken"))
|
||||
//! .message_handler(|ctx: HandlerCtx<Message>| async move {
|
||||
//! .message_handler(|ctx: DispatcherHandlerCtx<Message>| async move {
|
||||
//! ctx.reply("pong").await
|
||||
//! })
|
||||
//! .dispatch()
|
||||
|
@ -35,26 +34,26 @@
|
|||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [first-class construction]: https://stackoverflow.com/questions/646794/what-is-a-first-class-programming-construct
|
||||
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
//! [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener
|
||||
//! [10 update kinds]: crate::types::UpdateKind
|
||||
//! [`Update`]: crate::types::Update
|
||||
//! [`AsyncHandler`]: crate::dispatching::AsyncHandler
|
||||
//! [`LoggingHandler`]: crate::dispatching::LoggingHandler
|
||||
//! [`IgnoringHandler`]: crate::dispatching::IgnoringHandler
|
||||
//! [`IgnoringHandlerSafe`]: crate::dispatching::IgnoringHandlerSafe
|
||||
//! [for asynchronous functions]: crate::dispatching::AsyncHandler
|
||||
//! [`SessionDispatcher`]: crate::dispatching::session::SessionDispatcher
|
||||
//! [`ErrorHandler`]: crate::dispatching::ErrorHandler
|
||||
//! [`CtxHandler`]: crate::dispatching::CtxHandler
|
||||
//! [`SessionDispatcher`]: crate::dispatching::SessionDispatcher
|
||||
|
||||
mod async_handlers;
|
||||
mod ctx_handlers;
|
||||
mod dispatcher;
|
||||
mod handler_ctx;
|
||||
mod dispatcher_handler_ctx;
|
||||
mod error_handlers;
|
||||
mod middleware;
|
||||
pub mod session;
|
||||
pub mod update_listeners;
|
||||
|
||||
pub use async_handlers::{
|
||||
AsyncHandler, IgnoringHandler, IgnoringHandlerSafe, LoggingHandler,
|
||||
};
|
||||
pub use ctx_handlers::CtxHandler;
|
||||
pub use dispatcher::Dispatcher;
|
||||
pub use handler_ctx::HandlerCtx;
|
||||
pub use dispatcher_handler_ctx::DispatcherHandlerCtx;
|
||||
pub use error_handlers::{
|
||||
ErrorHandler, IgnoringErrorHandler, IgnoringErrorHandlerSafe,
|
||||
LoggingErrorHandler,
|
||||
};
|
||||
pub use middleware::Middleware;
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
//! 3. Your handler, which receives an update and turns your session into the
|
||||
//! next state.
|
||||
//! 4. [`SessionDispatcher`], which encapsulates your handler, [`Storage`], and
|
||||
//! implements [`AsyncHandler`].
|
||||
//! implements [`CtxHandler`].
|
||||
//!
|
||||
//! You supply [`SessionDispatcher`] into [`Dispatcher`]. Every time
|
||||
//! [`Dispatcher`] calls `SessionDispatcher::handle(...)`, the following steps
|
||||
//! are executed:
|
||||
//! [`Dispatcher`] calls `SessionDispatcher::handle_ctx(...)`, the following
|
||||
//! steps are executed:
|
||||
//!
|
||||
//! 1. If a storage doesn't contain a session from this chat, supply
|
||||
//! `Session::default()` into you handler, otherwise, supply the saved session
|
||||
|
@ -26,7 +26,7 @@
|
|||
//! [`SessionState::Exit`]:
|
||||
//! crate::dispatching::session::SessionState::Exit
|
||||
//! [`SessionState::Next`]: crate::dispatching::session::SessionState::Next
|
||||
//! [`AsyncHandler`]: crate::dispatching::AsyncHandler
|
||||
//! [`CtxHandler`]: crate::dispatching::CtxHandler
|
||||
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
|
||||
// TODO: examples
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::dispatching::{
|
|||
session::{
|
||||
GetChatId, InMemStorage, SessionHandlerCtx, SessionState, Storage,
|
||||
},
|
||||
AsyncHandler, HandlerCtx,
|
||||
CtxHandler, DispatcherHandlerCtx,
|
||||
};
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
|
@ -46,17 +46,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Session, H, Upd> AsyncHandler<HandlerCtx<Upd>, Result<(), ()>>
|
||||
impl<'a, Session, H, Upd> CtxHandler<DispatcherHandlerCtx<Upd>, Result<(), ()>>
|
||||
for SessionDispatcher<'a, Session, H>
|
||||
where
|
||||
H: AsyncHandler<SessionHandlerCtx<Upd, Session>, SessionState<Session>>,
|
||||
H: CtxHandler<SessionHandlerCtx<Upd, Session>, SessionState<Session>>,
|
||||
Upd: GetChatId,
|
||||
Session: Default,
|
||||
{
|
||||
/// Dispatches a single `message` from a private chat.
|
||||
fn handle<'b>(
|
||||
fn handle_ctx<'b>(
|
||||
&'b self,
|
||||
ctx: HandlerCtx<Upd>,
|
||||
ctx: DispatcherHandlerCtx<Upd>,
|
||||
) -> Pin<Box<dyn Future<Output = Result<(), ()>> + 'b>>
|
||||
where
|
||||
Upd: 'b,
|
||||
|
@ -72,7 +72,7 @@ where
|
|||
|
||||
if let SessionState::Next(new_session) = self
|
||||
.handler
|
||||
.handle(SessionHandlerCtx {
|
||||
.handle_ctx(SessionHandlerCtx {
|
||||
bot: ctx.bot,
|
||||
update: ctx.update,
|
||||
session,
|
||||
|
|
|
@ -5,7 +5,7 @@ pub use crate::{
|
|||
session::{
|
||||
GetChatId, SessionDispatcher, SessionHandlerCtx, SessionState,
|
||||
},
|
||||
Dispatcher, HandlerCtx,
|
||||
Dispatcher, DispatcherHandlerCtx,
|
||||
},
|
||||
requests::{Request, ResponseResult},
|
||||
types::Message,
|
||||
|
|
Loading…
Reference in a new issue