mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-24 23:57:38 +01:00
Improve the docs of teloxide::dispatching
This commit is contained in:
parent
32cab96af5
commit
a7dee4cab5
7 changed files with 225 additions and 159 deletions
|
@ -1,31 +0,0 @@
|
||||||
use std::{future::Future, pin::Pin};
|
|
||||||
|
|
||||||
/// An asynchronous polymorphic handler of a context.
|
|
||||||
///
|
|
||||||
/// Note that `AsyncHandler` is implemented for asynchronous `Fn`s, that consume
|
|
||||||
/// `Ctx` and return `Output`.
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
145
src/dispatching/async_handlers.rs
Normal file
145
src/dispatching/async_handlers.rs
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};
|
||||||
|
|
||||||
|
/// An asynchronous polymorphic handler of a context.
|
||||||
|
///
|
||||||
|
/// Note that `AsyncHandler` is implemented for asynchronous `Fn`s, that consume
|
||||||
|
/// `Ctx` and return `Output`.
|
||||||
|
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 {})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
dispatching::{
|
dispatching::{
|
||||||
error_handlers, update_listeners, update_listeners::UpdateListener,
|
update_listeners, update_listeners::UpdateListener, AsyncHandler,
|
||||||
AsyncHandler, HandlerCtx,
|
HandlerCtx, LoggingHandler,
|
||||||
},
|
},
|
||||||
types::{
|
types::{
|
||||||
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||||
|
@ -42,7 +42,9 @@ 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(error_handlers::Log),
|
handlers_error_handler: Box::new(LoggingHandler::new(
|
||||||
|
"An error from a Dispatcher's handler",
|
||||||
|
)),
|
||||||
message_handler: None,
|
message_handler: None,
|
||||||
edited_message_handler: None,
|
edited_message_handler: None,
|
||||||
channel_post_handler: None,
|
channel_post_handler: None,
|
||||||
|
@ -165,7 +167,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)),
|
||||||
&error_handlers::Log,
|
&LoggingHandler::new("An error from the update listener"),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
//! Commonly used handlers of errors.
|
|
||||||
|
|
||||||
use crate::dispatching::AsyncHandler;
|
|
||||||
use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};
|
|
||||||
|
|
||||||
/// A handler that silently ignores all errors.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// # #[tokio::main]
|
|
||||||
/// # async fn main_() {
|
|
||||||
/// use teloxide::dispatching::{error_handlers::Ignore, AsyncHandler};
|
|
||||||
///
|
|
||||||
/// Ignore.handle(()).await;
|
|
||||||
/// Ignore.handle(404).await;
|
|
||||||
/// Ignore.handle(String::from("error")).await;
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct Ignore;
|
|
||||||
|
|
||||||
impl<E> AsyncHandler<E, ()> for Ignore {
|
|
||||||
fn handle<'a>(&'a self, _: E) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
E: 'a,
|
|
||||||
{
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error 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::{AsyncHandler, error_handlers::IgnoreSafe};
|
|
||||||
///
|
|
||||||
/// let result: Result<String, Infallible> = "str".try_into();
|
|
||||||
/// match result {
|
|
||||||
/// Ok(string) => println!("{}", string),
|
|
||||||
/// Err(inf) => IgnoreSafe.handle(inf).await,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// IgnoreSafe.handle(return).await; // return type of `return` is `!` (aka never)
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```compile_fail
|
|
||||||
/// use teloxide::dispatching::dispatchers::filter::error_policy::{
|
|
||||||
/// ErrorPolicy, IgnoreSafe,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// IgnoreSafe.handle_error(0);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
|
|
||||||
/// [`Infallible`]: std::convert::Infallible
|
|
||||||
pub struct IgnoreSafe;
|
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
impl AsyncHandler<Infallible, ()> for IgnoreSafe {
|
|
||||||
fn handle<'a>(
|
|
||||||
&'a self,
|
|
||||||
_: Infallible,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
Infallible: 'a,
|
|
||||||
{
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error handler that prints all errors passed into it.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// # #[tokio::main]
|
|
||||||
/// # async fn main_() {
|
|
||||||
/// use teloxide::dispatching::{error_handlers::Log, AsyncHandler};
|
|
||||||
///
|
|
||||||
/// Log.handle(()).await;
|
|
||||||
/// Log.handle(404).await;
|
|
||||||
/// Log.handle(String::from("error")).await;
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct Log;
|
|
||||||
|
|
||||||
impl<E> AsyncHandler<E, ()> for Log
|
|
||||||
where
|
|
||||||
E: Debug,
|
|
||||||
{
|
|
||||||
fn handle<'a>(&'a self, error: E) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
E: 'a,
|
|
||||||
{
|
|
||||||
log::debug!("error: {:?}", error);
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,10 +6,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// A dispatcher's handler's context of a bot and an update.
|
/// A [`Dispatcher`]'s handler's context of a bot and an update.
|
||||||
///
|
///
|
||||||
/// See [the module-level documentation for the design
|
/// See [the module-level documentation for the design
|
||||||
/// overview](teloxide::dispatching).
|
/// overview](crate::dispatching).
|
||||||
|
///
|
||||||
|
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||||
pub struct HandlerCtx<Upd> {
|
pub struct HandlerCtx<Upd> {
|
||||||
pub bot: Arc<Bot>,
|
pub bot: Arc<Bot>,
|
||||||
pub update: Upd,
|
pub update: Upd,
|
||||||
|
|
|
@ -1,12 +1,60 @@
|
||||||
//! Update dispatching.
|
//! Update dispatching.
|
||||||
//!
|
//!
|
||||||
mod async_handler;
|
//! 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.
|
||||||
|
//! 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...
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//! The ping-pong bot ([full](https://github.com/teloxide/teloxide/blob/dev/examples/ping_pong_bot/)):
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # #[tokio::main]
|
||||||
|
//! # async fn main_() {
|
||||||
|
//! use teloxide::prelude::*;
|
||||||
|
//!
|
||||||
|
//! // Setup logging here...
|
||||||
|
//!
|
||||||
|
//! Dispatcher::new(Bot::new("MyAwesomeToken"))
|
||||||
|
//! .message_handler(|ctx: HandlerCtx<Message>| async move {
|
||||||
|
//! ctx.reply("pong").await
|
||||||
|
//! })
|
||||||
|
//! .dispatch()
|
||||||
|
//! .await;
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [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
|
||||||
|
|
||||||
|
mod async_handlers;
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
pub mod error_handlers;
|
|
||||||
mod handler_ctx;
|
mod handler_ctx;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod update_listeners;
|
pub mod update_listeners;
|
||||||
|
|
||||||
pub use async_handler::AsyncHandler;
|
pub use async_handlers::{
|
||||||
|
AsyncHandler, IgnoringHandler, IgnoringHandlerSafe, LoggingHandler,
|
||||||
|
};
|
||||||
pub use dispatcher::Dispatcher;
|
pub use dispatcher::Dispatcher;
|
||||||
pub use handler_ctx::HandlerCtx;
|
pub use handler_ctx::HandlerCtx;
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
//! Dispatching user sessions.
|
//! Dealing with dialogues.
|
||||||
//!
|
//!
|
||||||
//! There are four main components:
|
//! There are four main components:
|
||||||
//!
|
//!
|
||||||
//! 1. Your session type `Session`, which designates a dialogue state at the
|
//! 1. Your session type `Session`, which designates a dialogue state at the
|
||||||
//! current moment.
|
//! current moment.
|
||||||
//! 2. [`Storage`] that encapsulates all the sessions.
|
//! 2. [`Storage`], which encapsulates all the sessions.
|
||||||
//! 3. Your handler of type `H: async Fn(Session, Update) ->
|
//! 3. Your handler, which receives an update and turns your session into the
|
||||||
//! SessionState<Session>` that receives an update and turns your session into
|
//! next state.
|
||||||
//! the next state.
|
//! 4. [`SessionDispatcher`], which encapsulates your handler, [`Storage`], and
|
||||||
//! 4. [`SessionDispatcher`], which encapsulates your handler and
|
//! implements [`AsyncHandler`].
|
||||||
//! [`Storage`], and has the [`dispatch(Bot, Upd)`] function.
|
|
||||||
//!
|
//!
|
||||||
//! Every time you call `.dispatch(bot, update)` on your dispatcher, the
|
//! You supply [`SessionDispatcher`] into [`Dispatcher`]. Every time
|
||||||
//! following steps are executed:
|
//! [`Dispatcher`] calls `SessionDispatcher::handle(...)`, the following 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 previous
|
//! `Session::default()` into you handler, otherwise, supply the saved session
|
||||||
//! session.
|
//! from this chat.
|
||||||
//! 3. If a handler has returned [`SessionState::Terminate`], remove the
|
//! 3. If a handler has returned [`SessionState::Exit`], remove the session
|
||||||
//! session from a storage, otherwise force the storage to update the session.
|
//! from the storage, otherwise ([`SessionState::Next`]) force the storage to
|
||||||
|
//! update the session.
|
||||||
//!
|
//!
|
||||||
//! [`Storage`]: crate::dispatching::session::Storage
|
//! [`Storage`]: crate::dispatching::session::Storage
|
||||||
//! [`SessionDispatcher`]: crate::dispatching::session::SessionDispatcher
|
//! [`SessionDispatcher`]: crate::dispatching::session::SessionDispatcher
|
||||||
//! [`dispatch(Bot, Upd)`]:
|
//! [`SessionState::Exit`]:
|
||||||
//! crate::dispatching::session::SessionDispatcher::dispatch
|
//! crate::dispatching::session::SessionState::Exit
|
||||||
//! [`SessionState::Terminate`]:
|
//! [`SessionState::Next`]: crate::dispatching::session::SessionState::Next
|
||||||
//! crate::dispatching::session::SessionState::Terminate
|
//! [`AsyncHandler`]: crate::dispatching::AsyncHandler
|
||||||
|
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||||
|
|
||||||
// TODO: examples
|
// TODO: examples
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue