mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +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::{
|
||||
dispatching::{
|
||||
error_handlers, update_listeners, update_listeners::UpdateListener,
|
||||
AsyncHandler, HandlerCtx,
|
||||
update_listeners, update_listeners::UpdateListener, AsyncHandler,
|
||||
HandlerCtx, LoggingHandler,
|
||||
},
|
||||
types::{
|
||||
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||
|
@ -42,7 +42,9 @@ where
|
|||
pub fn new(bot: Bot) -> Self {
|
||||
Self {
|
||||
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,
|
||||
edited_message_handler: None,
|
||||
channel_post_handler: None,
|
||||
|
@ -165,7 +167,7 @@ where
|
|||
pub async fn dispatch(&'a self) {
|
||||
self.dispatch_with_listener(
|
||||
update_listeners::polling_default(Arc::clone(&self.bot)),
|
||||
&error_handlers::Log,
|
||||
&LoggingHandler::new("An error from the update listener"),
|
||||
)
|
||||
.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;
|
||||
|
||||
/// 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
|
||||
/// overview](teloxide::dispatching).
|
||||
/// overview](crate::dispatching).
|
||||
///
|
||||
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
pub struct HandlerCtx<Upd> {
|
||||
pub bot: Arc<Bot>,
|
||||
pub update: Upd,
|
||||
|
|
|
@ -1,12 +1,60 @@
|
|||
//! 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;
|
||||
pub mod error_handlers;
|
||||
mod handler_ctx;
|
||||
pub mod session;
|
||||
pub mod update_listeners;
|
||||
|
||||
pub use async_handler::AsyncHandler;
|
||||
pub use async_handlers::{
|
||||
AsyncHandler, IgnoringHandler, IgnoringHandlerSafe, LoggingHandler,
|
||||
};
|
||||
pub use dispatcher::Dispatcher;
|
||||
pub use handler_ctx::HandlerCtx;
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
//! Dispatching user sessions.
|
||||
//! Dealing with dialogues.
|
||||
//!
|
||||
//! There are four main components:
|
||||
//!
|
||||
//! 1. Your session type `Session`, which designates a dialogue state at the
|
||||
//! current moment.
|
||||
//! 2. [`Storage`] that encapsulates all the sessions.
|
||||
//! 3. Your handler of type `H: async Fn(Session, Update) ->
|
||||
//! SessionState<Session>` that receives an update and turns your session into
|
||||
//! the next state.
|
||||
//! 4. [`SessionDispatcher`], which encapsulates your handler and
|
||||
//! [`Storage`], and has the [`dispatch(Bot, Upd)`] function.
|
||||
//! 2. [`Storage`], which encapsulates all the sessions.
|
||||
//! 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`].
|
||||
//!
|
||||
//! Every time you call `.dispatch(bot, update)` on your dispatcher, the
|
||||
//! following steps are executed:
|
||||
//! You supply [`SessionDispatcher`] into [`Dispatcher`]. Every time
|
||||
//! [`Dispatcher`] calls `SessionDispatcher::handle(...)`, 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 previous
|
||||
//! session.
|
||||
//! 3. If a handler has returned [`SessionState::Terminate`], remove the
|
||||
//! session from a storage, otherwise force the storage to update the session.
|
||||
//! `Session::default()` into you handler, otherwise, supply the saved session
|
||||
//! from this chat.
|
||||
//! 3. If a handler has returned [`SessionState::Exit`], remove the session
|
||||
//! from the storage, otherwise ([`SessionState::Next`]) force the storage to
|
||||
//! update the session.
|
||||
//!
|
||||
//! [`Storage`]: crate::dispatching::session::Storage
|
||||
//! [`SessionDispatcher`]: crate::dispatching::session::SessionDispatcher
|
||||
//! [`dispatch(Bot, Upd)`]:
|
||||
//! crate::dispatching::session::SessionDispatcher::dispatch
|
||||
//! [`SessionState::Terminate`]:
|
||||
//! crate::dispatching::session::SessionState::Terminate
|
||||
//! [`SessionState::Exit`]:
|
||||
//! crate::dispatching::session::SessionState::Exit
|
||||
//! [`SessionState::Next`]: crate::dispatching::session::SessionState::Next
|
||||
//! [`AsyncHandler`]: crate::dispatching::AsyncHandler
|
||||
//! [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||
|
||||
// TODO: examples
|
||||
|
||||
|
|
Loading…
Reference in a new issue