mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 17:52:12 +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::{
|
use crate::{
|
||||||
dispatching::{
|
dispatching::{
|
||||||
update_listeners, update_listeners::UpdateListener, AsyncHandler,
|
error_handlers::ErrorHandler, update_listeners,
|
||||||
HandlerCtx, LoggingHandler,
|
update_listeners::UpdateListener, CtxHandler, DispatcherHandlerCtx,
|
||||||
|
LoggingErrorHandler, Middleware,
|
||||||
},
|
},
|
||||||
types::{
|
types::{
|
||||||
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||||
PreCheckoutQuery, ShippingQuery, UpdateKind,
|
PreCheckoutQuery, ShippingQuery, Update, UpdateKind,
|
||||||
},
|
},
|
||||||
Bot,
|
Bot,
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::{stream, StreamExt};
|
||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
type H<'a, Upd, HandlerE> =
|
type H<'a, Upd, HandlerE> = Option<
|
||||||
Option<Box<dyn AsyncHandler<HandlerCtx<Upd>, Result<(), HandlerE>> + 'a>>;
|
Box<dyn CtxHandler<DispatcherHandlerCtx<Upd>, Result<(), HandlerE>> + 'a>,
|
||||||
|
>;
|
||||||
|
|
||||||
/// One dispatcher to rule them all.
|
/// One dispatcher to rule them all.
|
||||||
///
|
///
|
||||||
|
@ -22,7 +24,9 @@ type H<'a, Upd, HandlerE> =
|
||||||
pub struct Dispatcher<'a, HandlerE> {
|
pub struct Dispatcher<'a, HandlerE> {
|
||||||
bot: Arc<Bot>,
|
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>,
|
message_handler: H<'a, Message, HandlerE>,
|
||||||
edited_message_handler: H<'a, Message, HandlerE>,
|
edited_message_handler: H<'a, Message, HandlerE>,
|
||||||
|
@ -45,7 +49,8 @@ where
|
||||||
pub fn new(bot: Bot) -> Self {
|
pub fn new(bot: Bot) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bot: Arc::new(bot),
|
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",
|
"An error from a Dispatcher's handler",
|
||||||
)),
|
)),
|
||||||
message_handler: None,
|
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.
|
/// Registers a handler of errors, produced by other handlers.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn handlers_error_handler<T>(mut self, val: T) -> Self
|
pub fn handlers_error_handler<T>(mut self, val: T) -> Self
|
||||||
where
|
where
|
||||||
T: AsyncHandler<HandlerE, ()> + 'a,
|
T: ErrorHandler<HandlerE> + 'a,
|
||||||
{
|
{
|
||||||
self.handlers_error_handler = Box::new(val);
|
self.handlers_error_handler = Box::new(val);
|
||||||
self
|
self
|
||||||
|
@ -74,7 +89,7 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn message_handler<H>(mut self, h: H) -> Self
|
pub fn message_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||||
{
|
{
|
||||||
self.message_handler = Some(Box::new(h));
|
self.message_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -83,7 +98,7 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn edited_message_handler<H>(mut self, h: H) -> Self
|
pub fn edited_message_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||||
{
|
{
|
||||||
self.edited_message_handler = Some(Box::new(h));
|
self.edited_message_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -92,7 +107,7 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn channel_post_handler<H>(mut self, h: H) -> Self
|
pub fn channel_post_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<DispatcherHandlerCtx<Message>, Result<(), HandlerE>> + 'a,
|
||||||
{
|
{
|
||||||
self.channel_post_handler = Some(Box::new(h));
|
self.channel_post_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -101,7 +116,7 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn edited_channel_post_handler<H>(mut self, h: H) -> Self
|
pub fn edited_channel_post_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
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.edited_channel_post_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -110,7 +125,8 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn inline_query_handler<H>(mut self, h: H) -> Self
|
pub fn inline_query_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<InlineQuery>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<DispatcherHandlerCtx<InlineQuery>, Result<(), HandlerE>>
|
||||||
|
+ 'a,
|
||||||
{
|
{
|
||||||
self.inline_query_handler = Some(Box::new(h));
|
self.inline_query_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -119,8 +135,10 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn chosen_inline_result_handler<H>(mut self, h: H) -> Self
|
pub fn chosen_inline_result_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<ChosenInlineResult>, Result<(), HandlerE>>
|
H: CtxHandler<
|
||||||
+ 'a,
|
DispatcherHandlerCtx<ChosenInlineResult>,
|
||||||
|
Result<(), HandlerE>,
|
||||||
|
> + 'a,
|
||||||
{
|
{
|
||||||
self.chosen_inline_result_handler = Some(Box::new(h));
|
self.chosen_inline_result_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -129,7 +147,10 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn callback_query_handler<H>(mut self, h: H) -> Self
|
pub fn callback_query_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<CallbackQuery>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<
|
||||||
|
DispatcherHandlerCtx<CallbackQuery>,
|
||||||
|
Result<(), HandlerE>,
|
||||||
|
> + 'a,
|
||||||
{
|
{
|
||||||
self.callback_query_handler = Some(Box::new(h));
|
self.callback_query_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -138,7 +159,10 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn shipping_query_handler<H>(mut self, h: H) -> Self
|
pub fn shipping_query_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<ShippingQuery>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<
|
||||||
|
DispatcherHandlerCtx<ShippingQuery>,
|
||||||
|
Result<(), HandlerE>,
|
||||||
|
> + 'a,
|
||||||
{
|
{
|
||||||
self.shipping_query_handler = Some(Box::new(h));
|
self.shipping_query_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -147,8 +171,10 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn pre_checkout_query_handler<H>(mut self, h: H) -> Self
|
pub fn pre_checkout_query_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<PreCheckoutQuery>, Result<(), HandlerE>>
|
H: CtxHandler<
|
||||||
+ 'a,
|
DispatcherHandlerCtx<PreCheckoutQuery>,
|
||||||
|
Result<(), HandlerE>,
|
||||||
|
> + 'a,
|
||||||
{
|
{
|
||||||
self.pre_checkout_query_handler = Some(Box::new(h));
|
self.pre_checkout_query_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -157,7 +183,7 @@ where
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn poll_handler<H>(mut self, h: H) -> Self
|
pub fn poll_handler<H>(mut self, h: H) -> Self
|
||||||
where
|
where
|
||||||
H: AsyncHandler<HandlerCtx<Poll>, Result<(), HandlerE>> + 'a,
|
H: CtxHandler<DispatcherHandlerCtx<Poll>, Result<(), HandlerE>> + 'a,
|
||||||
{
|
{
|
||||||
self.poll_handler = Some(Box::new(h));
|
self.poll_handler = Some(Box::new(h));
|
||||||
self
|
self
|
||||||
|
@ -170,7 +196,7 @@ where
|
||||||
pub async fn dispatch(&'a self) {
|
pub async fn dispatch(&'a self) {
|
||||||
self.dispatch_with_listener(
|
self.dispatch_with_listener(
|
||||||
update_listeners::polling_default(Arc::clone(&self.bot)),
|
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;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +209,7 @@ where
|
||||||
update_listener_error_handler: &'a Eh,
|
update_listener_error_handler: &'a Eh,
|
||||||
) where
|
) where
|
||||||
UListener: UpdateListener<ListenerE> + 'a,
|
UListener: UpdateListener<ListenerE> + 'a,
|
||||||
Eh: AsyncHandler<ListenerE, ()> + 'a,
|
Eh: ErrorHandler<ListenerE> + 'a,
|
||||||
ListenerE: Debug,
|
ListenerE: Debug,
|
||||||
{
|
{
|
||||||
let update_listener = Box::pin(update_listener);
|
let update_listener = Box::pin(update_listener);
|
||||||
|
@ -193,11 +219,17 @@ where
|
||||||
let update = match update {
|
let update = match update {
|
||||||
Ok(update) => update,
|
Ok(update) => update,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
update_listener_error_handler.handle(error).await;
|
update_listener_error_handler.handle_error(error).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let update = stream::iter(&self.middlewares)
|
||||||
|
.fold(update, |acc, middleware| async move {
|
||||||
|
middleware.handle(acc).await
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
match update.kind {
|
match update.kind {
|
||||||
UpdateKind::Message(message) => {
|
UpdateKind::Message(message) => {
|
||||||
self.handle(&self.message_handler, message).await
|
self.handle(&self.message_handler, message).await
|
||||||
|
@ -241,13 +273,13 @@ where
|
||||||
async fn handle<Upd>(&self, handler: &H<'a, Upd, HandlerE>, update: Upd) {
|
async fn handle<Upd>(&self, handler: &H<'a, Upd, HandlerE>, update: Upd) {
|
||||||
if let Some(handler) = &handler {
|
if let Some(handler) = &handler {
|
||||||
if let Err(error) = handler
|
if let Err(error) = handler
|
||||||
.handle(HandlerCtx {
|
.handle_ctx(DispatcherHandlerCtx {
|
||||||
bot: Arc::clone(&self.bot),
|
bot: Arc::clone(&self.bot),
|
||||||
update,
|
update,
|
||||||
})
|
})
|
||||||
.await
|
.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).
|
/// overview](crate::dispatching).
|
||||||
///
|
///
|
||||||
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||||
pub struct HandlerCtx<Upd> {
|
pub struct DispatcherHandlerCtx<Upd> {
|
||||||
pub bot: Arc<Bot>,
|
pub bot: Arc<Bot>,
|
||||||
pub update: Upd,
|
pub update: Upd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Upd> GetChatId for HandlerCtx<Upd>
|
impl<Upd> GetChatId for DispatcherHandlerCtx<Upd>
|
||||||
where
|
where
|
||||||
Upd: GetChatId,
|
Upd: GetChatId,
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HandlerCtx<Message> {
|
impl DispatcherHandlerCtx<Message> {
|
||||||
pub async fn reply<T>(&self, text: T) -> ResponseResult<()>
|
pub async fn reply<T>(&self, text: T) -> ResponseResult<()>
|
||||||
where
|
where
|
||||||
T: Into<String>,
|
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.
|
//! Updates dispatching.
|
||||||
//!
|
//!
|
||||||
//! The key type here is [`Dispatcher`]. It encapsulates [`UpdateListener`], a
|
//! The key type here is [`Dispatcher`]. It encapsulates middlewares, handlers
|
||||||
//! handler of errors, and handlers for [10 update kinds]. When [`Update`] is
|
//! for [10 update kinds], and [`ErrorHandler`] for them. When [`Update`] is
|
||||||
//! received from Telegram, it is supplied to an appropriate handler, and if a
|
//! received from Telegram, the following steps are executed:
|
||||||
//! handler has returned an error, the error is supplied into an error handler.
|
//!
|
||||||
|
//! 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!
|
//! That's simple!
|
||||||
//!
|
//!
|
||||||
//! All the handlers are of type [`AsyncHandler`]. It's like a [first-class
|
//! Note that handlers implement [`CtxHandler`], which means that you are able
|
||||||
//! construction] in this module, because:
|
//! to supply [`SessionDispatcher`] as a handler, since it implements
|
||||||
//! 1. It is implemented for [`SessionDispatcher`], which itself accepts
|
//! [`CtxHandler`] too!
|
||||||
//! [`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...
|
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//! The ping-pong bot ([full](https://github.com/teloxide/teloxide/blob/dev/examples/ping_pong_bot/)):
|
//! The ping-pong bot ([full](https://github.com/teloxide/teloxide/blob/dev/examples/ping_pong_bot/)):
|
||||||
|
@ -27,7 +26,7 @@
|
||||||
//! // Setup logging here...
|
//! // Setup logging here...
|
||||||
//!
|
//!
|
||||||
//! Dispatcher::new(Bot::new("MyAwesomeToken"))
|
//! Dispatcher::new(Bot::new("MyAwesomeToken"))
|
||||||
//! .message_handler(|ctx: HandlerCtx<Message>| async move {
|
//! .message_handler(|ctx: DispatcherHandlerCtx<Message>| async move {
|
||||||
//! ctx.reply("pong").await
|
//! ctx.reply("pong").await
|
||||||
//! })
|
//! })
|
||||||
//! .dispatch()
|
//! .dispatch()
|
||||||
|
@ -35,26 +34,26 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [first-class construction]: https://stackoverflow.com/questions/646794/what-is-a-first-class-programming-construct
|
|
||||||
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||||
//! [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener
|
|
||||||
//! [10 update kinds]: crate::types::UpdateKind
|
//! [10 update kinds]: crate::types::UpdateKind
|
||||||
//! [`Update`]: crate::types::Update
|
//! [`Update`]: crate::types::Update
|
||||||
//! [`AsyncHandler`]: crate::dispatching::AsyncHandler
|
//! [`ErrorHandler`]: crate::dispatching::ErrorHandler
|
||||||
//! [`LoggingHandler`]: crate::dispatching::LoggingHandler
|
//! [`CtxHandler`]: crate::dispatching::CtxHandler
|
||||||
//! [`IgnoringHandler`]: crate::dispatching::IgnoringHandler
|
//! [`SessionDispatcher`]: crate::dispatching::SessionDispatcher
|
||||||
//! [`IgnoringHandlerSafe`]: crate::dispatching::IgnoringHandlerSafe
|
|
||||||
//! [for asynchronous functions]: crate::dispatching::AsyncHandler
|
|
||||||
//! [`SessionDispatcher`]: crate::dispatching::session::SessionDispatcher
|
|
||||||
|
|
||||||
mod async_handlers;
|
mod ctx_handlers;
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
mod handler_ctx;
|
mod dispatcher_handler_ctx;
|
||||||
|
mod error_handlers;
|
||||||
|
mod middleware;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod update_listeners;
|
pub mod update_listeners;
|
||||||
|
|
||||||
pub use async_handlers::{
|
pub use ctx_handlers::CtxHandler;
|
||||||
AsyncHandler, IgnoringHandler, IgnoringHandlerSafe, LoggingHandler,
|
|
||||||
};
|
|
||||||
pub use dispatcher::Dispatcher;
|
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
|
//! 3. Your handler, which receives an update and turns your session into the
|
||||||
//! next state.
|
//! next state.
|
||||||
//! 4. [`SessionDispatcher`], which encapsulates your handler, [`Storage`], and
|
//! 4. [`SessionDispatcher`], which encapsulates your handler, [`Storage`], and
|
||||||
//! implements [`AsyncHandler`].
|
//! implements [`CtxHandler`].
|
||||||
//!
|
//!
|
||||||
//! You supply [`SessionDispatcher`] into [`Dispatcher`]. Every time
|
//! You supply [`SessionDispatcher`] into [`Dispatcher`]. Every time
|
||||||
//! [`Dispatcher`] calls `SessionDispatcher::handle(...)`, the following steps
|
//! [`Dispatcher`] calls `SessionDispatcher::handle_ctx(...)`, the following
|
||||||
//! are executed:
|
//! steps are executed:
|
||||||
//!
|
//!
|
||||||
//! 1. If a storage doesn't contain a session from this chat, supply
|
//! 1. If a storage doesn't contain a session from this chat, supply
|
||||||
//! `Session::default()` into you handler, otherwise, supply the saved session
|
//! `Session::default()` into you handler, otherwise, supply the saved session
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
//! [`SessionState::Exit`]:
|
//! [`SessionState::Exit`]:
|
||||||
//! crate::dispatching::session::SessionState::Exit
|
//! crate::dispatching::session::SessionState::Exit
|
||||||
//! [`SessionState::Next`]: crate::dispatching::session::SessionState::Next
|
//! [`SessionState::Next`]: crate::dispatching::session::SessionState::Next
|
||||||
//! [`AsyncHandler`]: crate::dispatching::AsyncHandler
|
//! [`CtxHandler`]: crate::dispatching::CtxHandler
|
||||||
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||||
|
|
||||||
// TODO: examples
|
// TODO: examples
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::dispatching::{
|
||||||
session::{
|
session::{
|
||||||
GetChatId, InMemStorage, SessionHandlerCtx, SessionState, Storage,
|
GetChatId, InMemStorage, SessionHandlerCtx, SessionState, Storage,
|
||||||
},
|
},
|
||||||
AsyncHandler, HandlerCtx,
|
CtxHandler, DispatcherHandlerCtx,
|
||||||
};
|
};
|
||||||
use std::{future::Future, pin::Pin};
|
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>
|
for SessionDispatcher<'a, Session, H>
|
||||||
where
|
where
|
||||||
H: AsyncHandler<SessionHandlerCtx<Upd, Session>, SessionState<Session>>,
|
H: CtxHandler<SessionHandlerCtx<Upd, Session>, SessionState<Session>>,
|
||||||
Upd: GetChatId,
|
Upd: GetChatId,
|
||||||
Session: Default,
|
Session: Default,
|
||||||
{
|
{
|
||||||
/// Dispatches a single `message` from a private chat.
|
/// Dispatches a single `message` from a private chat.
|
||||||
fn handle<'b>(
|
fn handle_ctx<'b>(
|
||||||
&'b self,
|
&'b self,
|
||||||
ctx: HandlerCtx<Upd>,
|
ctx: DispatcherHandlerCtx<Upd>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<(), ()>> + 'b>>
|
) -> Pin<Box<dyn Future<Output = Result<(), ()>> + 'b>>
|
||||||
where
|
where
|
||||||
Upd: 'b,
|
Upd: 'b,
|
||||||
|
@ -72,7 +72,7 @@ where
|
||||||
|
|
||||||
if let SessionState::Next(new_session) = self
|
if let SessionState::Next(new_session) = self
|
||||||
.handler
|
.handler
|
||||||
.handle(SessionHandlerCtx {
|
.handle_ctx(SessionHandlerCtx {
|
||||||
bot: ctx.bot,
|
bot: ctx.bot,
|
||||||
update: ctx.update,
|
update: ctx.update,
|
||||||
session,
|
session,
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub use crate::{
|
||||||
session::{
|
session::{
|
||||||
GetChatId, SessionDispatcher, SessionHandlerCtx, SessionState,
|
GetChatId, SessionDispatcher, SessionHandlerCtx, SessionState,
|
||||||
},
|
},
|
||||||
Dispatcher, HandlerCtx,
|
Dispatcher, DispatcherHandlerCtx,
|
||||||
},
|
},
|
||||||
requests::{Request, ResponseResult},
|
requests::{Request, ResponseResult},
|
||||||
types::Message,
|
types::Message,
|
||||||
|
|
Loading…
Reference in a new issue