Implement the CommandRepl trait

This commit is contained in:
Hirrolot 2022-10-11 14:54:51 +06:00
parent 512e785999
commit 739772929b
8 changed files with 174 additions and 6 deletions

View file

@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## unreleased ## unreleased
### Added
- `teloxide::dispatching::repls::CommandRepl`, `teloxide::prelude::CommandRepl` ([issue #740](https://github.com/teloxide/teloxide/issues/740))
### Deprecated
- `teloxide::dispatching::repls::{commands_repl, commands_repl_with_listener}` (use `CommandRepl` instead)
## 0.11.0 - 2022-10-07 ## 0.11.0 - 2022-10-07
### Changed ### Changed

View file

@ -1,6 +1,22 @@
This document describes breaking changes of `teloxide` crate, as well as the ways to update code. This document describes breaking changes of `teloxide` crate, as well as the ways to update code.
Note that the list of required changes is not fully exhaustive and it may lack something in rare cases. Note that the list of required changes is not fully exhaustive and it may lack something in rare cases.
## 0.10 -> 0.xxx.xxx
### teloxide
We have introduced the new trait `CommandRepl` that replaces the old `commands_repl_(with_listener)` functions:
```diff,rust
- teloxide::commands_repl(bot, answer, Command::ty())
+ Command::repl(bot, answer)
```
```diff,rust
- teloxide::commands_repl_with_listener(bot, answer, listener, Command::ty())
+ Command::repl_with_listener(bot, answer, listener)
```
## 0.10 -> 0.11 ## 0.10 -> 0.11
### core ### core

View file

@ -60,7 +60,7 @@ async fn main() {
let bot = teloxide::Bot::from_env(); let bot = teloxide::Bot::from_env();
teloxide::commands_repl(bot, action, Command::ty()).await; Command::repl(bot, action).await;
} }
async fn action(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> { async fn action(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {

View file

@ -7,7 +7,7 @@ async fn main() {
let bot = Bot::from_env(); let bot = Bot::from_env();
teloxide::commands_repl(bot, answer, Command::ty()).await; Command::repl(bot, answer).await;
} }
#[derive(BotCommands, Clone)] #[derive(BotCommands, Clone)]

View file

@ -11,5 +11,7 @@
mod commands_repl; mod commands_repl;
mod repl; mod repl;
pub use commands_repl::CommandRepl;
#[allow(deprecated)]
pub use commands_repl::{commands_repl, commands_repl_with_listener}; pub use commands_repl::{commands_repl, commands_repl_with_listener};
pub use repl::{repl, repl_with_listener}; pub use repl::{repl, repl_with_listener};

View file

@ -8,8 +8,145 @@ use crate::{
utils::command::BotCommands, utils::command::BotCommands,
}; };
use dptree::di::{DependencyMap, Injectable}; use dptree::di::{DependencyMap, Injectable};
use futures::future::BoxFuture;
use std::{fmt::Debug, marker::PhantomData}; use std::{fmt::Debug, marker::PhantomData};
/// A [REPL] for commands.
///
/// REPLs are meant only for simple bots and rapid prototyping. If you need to
/// supply dependencies or describe more complex dispatch logic, please use
/// [`Dispatcher`]. See also: ["Dispatching or
/// REPLs?"](../index.html#dispatching-or-repls).
///
/// [`Dispatcher`]: crate::dispatching::Dispatcher
///
/// All errors from the handler and update listener will be logged.
///
/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop
///
/// This trait extends your [`BotCommands`] type with REPL facilities.
///
/// ## Signatures
///
/// Don't be scared by many trait bounds in the signatures, in essence they
/// require:
///
/// 1. `bot` is a bot, client for the Telegram bot API. It is represented via
/// the [`Requester`] trait.
/// 2. `handler` is an `async` function that takes arguments from
/// [`DependencyMap`] (see below) and returns [`ResponseResult`].
/// 3. `listener` is something that takes updates from a Telegram server and
/// implements [`UpdateListener`].
///
/// All the other requirements are about thread safety and data validity and can
/// be ignored for most of the time.
///
/// ## Handler arguments
///
/// `teloxide` provides the following types to the `handler`:
/// - [`Message`]
/// - `R` (type of the `bot`)
/// - `Cmd` (type of the parsed command)
/// - [`Me`]
///
/// Each of these types can be accepted as a handler parameter. Note that they
/// aren't all required at the same time: e.g., you can take only the bot and
/// the command without [`Me`] and [`Message`].
///
/// [`Me`]: crate::types::Me
/// [`Message`]: crate::types::Message
///
/// ## Stopping
//
#[doc = include_str!("stopping.md")]
///
/// ## Caution
//
#[doc = include_str!("caution.md")]
///
#[cfg(feature = "ctrlc_handler")]
pub trait CommandRepl {
/// A REPL for commands.
///
/// See [`CommandRepl`] for more details.
fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
where
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetUpdates: Send,
<R as Requester>::GetWebhookInfo: Send,
<R as Requester>::GetMe: Send,
<R as Requester>::DeleteWebhook: Send,
H: Injectable<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static;
/// A REPL for commands with a custom [`UpdateListener`].
///
/// See [`CommandRepl`] for more details.
fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) -> BoxFuture<'a, ()>
where
H: Injectable<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static,
L: UpdateListener + Send + 'a,
L::Err: Debug + Send + 'a,
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetMe: Send;
}
#[cfg(feature = "ctrlc_handler")]
impl<Cmd> CommandRepl for Cmd
where
Cmd: BotCommands + Send + Sync + 'static,
{
fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
where
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetUpdates: Send,
<R as Requester>::GetWebhookInfo: Send,
<R as Requester>::GetMe: Send,
<R as Requester>::DeleteWebhook: Send,
H: Injectable<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static,
{
let cloned_bot = bot.clone();
Box::pin(async move {
Self::repl_with_listener(
bot,
handler,
update_listeners::polling_default(cloned_bot).await,
)
.await
})
}
fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) -> BoxFuture<'a, ()>
where
H: Injectable<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static,
L: UpdateListener + Send + 'a,
L::Err: Debug + Send + 'a,
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetMe: Send,
{
use crate::dispatching::Dispatcher;
// Other update types are of no interest to use since this REPL is only for
// commands. See <https://github.com/teloxide/teloxide/issues/557>.
let ignore_update = |_upd| Box::pin(async {});
Box::pin(async move {
Dispatcher::builder(
bot,
Update::filter_message().filter_command::<Cmd>().endpoint(handler),
)
.default_handler(ignore_update)
.enable_ctrlc_handler()
.build()
.dispatch_with_listener(
listener,
LoggingErrorHandler::with_custom_text("An error from the update listener"),
)
.await
})
}
}
/// A [REPL] for commands. /// A [REPL] for commands.
// //
/// ///
@ -59,6 +196,7 @@ use std::{fmt::Debug, marker::PhantomData};
#[doc = include_str!("caution.md")] #[doc = include_str!("caution.md")]
/// ///
#[cfg(feature = "ctrlc_handler")] #[cfg(feature = "ctrlc_handler")]
#[deprecated(note = "Use `CommandsRepl::repl` instead")]
pub async fn commands_repl<'a, R, Cmd, H, Args>(bot: R, handler: H, cmd: PhantomData<Cmd>) pub async fn commands_repl<'a, R, Cmd, H, Args>(bot: R, handler: H, cmd: PhantomData<Cmd>)
where where
R: Requester + Clone + Send + Sync + 'static, R: Requester + Clone + Send + Sync + 'static,
@ -68,6 +206,7 @@ where
{ {
let cloned_bot = bot.clone(); let cloned_bot = bot.clone();
#[allow(deprecated)]
commands_repl_with_listener( commands_repl_with_listener(
bot, bot,
handler, handler,
@ -127,6 +266,7 @@ where
#[doc = include_str!("caution.md")] #[doc = include_str!("caution.md")]
/// ///
#[cfg(feature = "ctrlc_handler")] #[cfg(feature = "ctrlc_handler")]
#[deprecated(note = "Use `CommandsRepl::repl_with_listener` instead")]
pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, Args>( pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, Args>(
bot: R, bot: R,
handler: H, handler: H,

View file

@ -58,9 +58,10 @@
#![allow(clippy::nonstandard_macro_braces)] #![allow(clippy::nonstandard_macro_braces)]
#[cfg(feature = "ctrlc_handler")] #[cfg(feature = "ctrlc_handler")]
pub use dispatching::repls::{ pub use dispatching::repls::{repl, repl_with_listener};
commands_repl, commands_repl_with_listener, repl, repl_with_listener,
}; #[allow(deprecated)]
pub use dispatching::repls::{commands_repl, commands_repl_with_listener};
pub mod dispatching; pub mod dispatching;
pub mod error_handlers; pub mod error_handlers;

View file

@ -6,7 +6,8 @@ pub use crate::error_handlers::{LoggingErrorHandler, OnError};
pub use crate::respond; pub use crate::respond;
pub use crate::dispatching::{ pub use crate::dispatching::{
dialogue::Dialogue, Dispatcher, HandlerExt as _, MessageFilterExt as _, UpdateFilterExt as _, dialogue::Dialogue, repls::CommandRepl as _, Dispatcher, HandlerExt as _,
MessageFilterExt as _, UpdateFilterExt as _,
}; };
pub use teloxide_core::{ pub use teloxide_core::{