From 72dfef49633a611b262a6037c5a7002414208f5a Mon Sep 17 00:00:00 2001 From: p0lunin Date: Wed, 29 Dec 2021 13:33:51 +0200 Subject: [PATCH] added DialogueState macro --- Cargo.toml | 4 +- README.md | 10 ++--- examples/dialogue_bot/src/main.rs | 63 ++++++++-------------------- src/dispatching2/handler_ext.rs | 16 ++++---- src/dispatching2/handler_factory.rs | 5 +-- src/dispatching2/mod.rs | 2 +- src/lib.rs | 3 ++ src/prelude.rs | 3 +- tests/bot_dialogue.rs | 64 +++++++++++++++++++++++++++++ 9 files changed, 104 insertions(+), 66 deletions(-) create mode 100644 tests/bot_dialogue.rs diff --git a/Cargo.toml b/Cargo.toml index c7000b43..d210703e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,8 +73,8 @@ full = [ [dependencies] teloxide-core = { version = "0.3.3", default-features = false } #teloxide-core = { git = "https://github.com/teloxide/teloxide-core.git", rev = "...", default-features = false } -teloxide-macros = { version = "0.4", optional = true } - +#teloxide-macros = { version = "0.4", optional = true } +teloxide-macros = { path = "../teloxide-macros", optional = true } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/README.md b/README.md index 818be122..db0b46c9 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ A dialogue is described by an enumeration where each variant is one of possible Below is a bot that asks you three questions and then sends the answers back to you. First, let's start with an enumeration (a collection of our dialogue's states): -([dialogue_bot/src/dialogue/mod.rs](./examples/dialogue_bot/src/dialogue/mod.rs)) +([dialogue_bot/src/dialogue/mod.rs](examples/dialogue_bot/src/state/mod.rs)) ```rust,ignore // Imports are omitted... @@ -197,7 +197,7 @@ When a user sends a message to our bot and such a dialogue does not exist yet, a
Dialogue::Start -([dialogue_bot/src/dialogue/states/start.rs](./examples/dialogue_bot/src/dialogue/states/start.rs)) +([dialogue_bot/src/dialogue/states/start.rs](examples/dialogue_bot/src/state/states/start.rs)) ```rust,ignore // Imports are omitted... @@ -219,7 +219,7 @@ async fn start(
Dialogue::ReceiveFullName -([dialogue_bot/src/dialogue/states/receive_full_name.rs](./examples/dialogue_bot/src/dialogue/states/receive_full_name.rs)) +([dialogue_bot/src/dialogue/states/receive_full_name.rs](examples/dialogue_bot/src/state/states/receive_full_name.rs)) ```rust,ignore // Imports are omitted... @@ -242,7 +242,7 @@ async fn receive_full_name(
Dialogue::ReceiveAge -([dialogue_bot/src/dialogue/states/receive_age.rs](./examples/dialogue_bot/src/dialogue/states/receive_age.rs)) +([dialogue_bot/src/dialogue/states/receive_age.rs](examples/dialogue_bot/src/state/states/receive_age.rs)) ```rust,ignore // Imports are omitted... @@ -275,7 +275,7 @@ async fn receive_age_state(
Dialogue::ReceiveLocation -([dialogue_bot/src/dialogue/states/receive_location.rs](./examples/dialogue_bot/src/dialogue/states/receive_location.rs)) +([dialogue_bot/src/dialogue/states/receive_location.rs](examples/dialogue_bot/src/state/states/receive_location.rs)) ```rust,ignore // Imports are omitted... diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index f312a9d5..ce2981b1 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -15,62 +15,36 @@ // ``` use teloxide::{ dispatching2::dialogue::{serializer::Json, SqliteStorage}, + macros::DialogueState, prelude::*, }; -use teloxide::dispatching2::HandlerFactory; // FIXME: naming type MyBot = AutoSend; type Store = SqliteStorage; type BotDialogue = Dialogue; -#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[derive(DialogueState, Clone, serde::Serialize, serde::Deserialize)] +#[out(anyhow::Result<()>)] +#[store(SqliteStorage)] pub enum State { + #[handler(handle_start)] Start, + + #[handler(handle_receive_full_name)] ReceiveFullName, + + #[handler(handle_receive_age)] ReceiveAge(String), + + #[handler(handle_receive_location)] ReceiveLocation(ReceiveLocation), } #[derive(Clone, serde::Serialize, serde::Deserialize)] -pub struct ReceiveLocation { full_name: String, age: u8 } - -impl HandlerFactory for State { - type Out = anyhow::Result<()>; - - fn handler() -> dptree::Handler<'static, DependencyMap, anyhow::Result<()>> { - dptree::entry() - .branch( - dptree::filter(|dialogue: Dialogue| async move { - let state = match dialogue.current_state_or_default().await { - Ok(state) => state, - Err(_) => return false, - }; - match state { State::Start => true, _ => false } - }).endpoint(handle_start) - ) - .branch( - dptree::filter(|dialogue: Dialogue| async move { - let state = match dialogue.current_state_or_default().await { - Ok(state) => state, - Err(_) => return false, - }; - match state { State::ReceiveFullName => true, _ => false } - }).endpoint(handle_receive_full_name) - ) - .branch( - dptree::filter_map(|dialogue: Dialogue| async move { - let state = dialogue.current_state_or_default().await.ok()?; - match state { State::ReceiveAge(arg) => Some(arg), _ => None } - }).endpoint(handle_receive_age) - ) - .branch( - dptree::filter_map(|dialogue: Dialogue| async move { - let state = dialogue.current_state_or_default().await.ok()?; - match state { State::ReceiveLocation(arg) => Some(arg), _ => None } - }).endpoint(handle_receive_location) - ) - } +pub struct ReceiveLocation { + full_name: String, + age: u8, } impl Default for State { @@ -89,9 +63,7 @@ async fn main() { Dispatcher::new(bot) .dependencies(dptree::deps![storage]) - .messages_handler(|h| { - h.add_dialogue::().dispatch_by::() - }) + .messages_handler(|h| h.add_dialogue::().dispatch_by::()) .dispatch() .await; } @@ -134,10 +106,11 @@ async fn handle_receive_location( bot: MyBot, mes: Message, dialogue: BotDialogue, - state: ReceiveLocation + state: ReceiveLocation, ) -> anyhow::Result<()> { let location = mes.text().unwrap(); - let message = format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, location); + let message = + format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, location); bot.send_message(mes.chat_id(), message).await?; dialogue.exit().await?; Ok(()) diff --git a/src/dispatching2/handler_ext.rs b/src/dispatching2/handler_ext.rs index 3edf0963..873c8aa9 100644 --- a/src/dispatching2/handler_ext.rs +++ b/src/dispatching2/handler_ext.rs @@ -1,9 +1,5 @@ -use crate::{types::Message, utils::command::BotCommand}; -use dptree::{ - Handler, -}; -use crate::dispatching2::HandlerFactory; -use dptree::di::DependencyMap; +use crate::{dispatching2::HandlerFactory, types::Message, utils::command::BotCommand}; +use dptree::{di::DependencyMap, Handler}; pub trait HandlerExt { fn add_command(self, bot_name: String) -> Self @@ -11,7 +7,8 @@ pub trait HandlerExt { C: BotCommand + Send + Sync + 'static; fn dispatch_by(self) -> Self - where F: HandlerFactory; + where + F: HandlerFactory; } impl HandlerExt for Handler<'static, DependencyMap, Output> @@ -28,7 +25,10 @@ where })) } - fn dispatch_by(self) -> Self where F: HandlerFactory { + fn dispatch_by(self) -> Self + where + F: HandlerFactory, + { self.chain(F::handler()) } } diff --git a/src/dispatching2/handler_factory.rs b/src/dispatching2/handler_factory.rs index fe1d5399..4d1590d8 100644 --- a/src/dispatching2/handler_factory.rs +++ b/src/dispatching2/handler_factory.rs @@ -1,8 +1,7 @@ -use dptree::di::DependencyMap; -use dptree::Handler; +use dptree::{di::DependencyMap, Handler}; pub trait HandlerFactory { type Out; fn handler() -> Handler<'static, DependencyMap, Self::Out>; -} \ No newline at end of file +} diff --git a/src/dispatching2/mod.rs b/src/dispatching2/mod.rs index 847d44ea..9daeb26c 100644 --- a/src/dispatching2/mod.rs +++ b/src/dispatching2/mod.rs @@ -6,5 +6,5 @@ mod handler_ext; mod handler_factory; pub use dispatcher::Dispatcher; -pub use handler_factory::HandlerFactory; pub use handler_ext::HandlerExt; +pub use handler_factory::HandlerFactory; diff --git a/src/lib.rs b/src/lib.rs index d34b1366..643544b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,9 @@ pub use teloxide_core::*; #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "macros")))] pub use teloxide_macros as macros; +#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "new-dispatching")))] +#[cfg(feature = "new-dispatching")] +pub use dptree; #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "macros")))] #[cfg(feature = "macros")] pub use teloxide_macros::teloxide; diff --git a/src/prelude.rs b/src/prelude.rs index a91827c3..52569c79 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -17,8 +17,7 @@ pub use crate::dispatching::{ #[cfg(feature = "new-dispatching")] pub use crate::dispatching2::{ dialogue::{Dialogue, DialogueHandlerExt as _}, - HandlerExt as _, - Dispatcher, + Dispatcher, HandlerExt as _, }; #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "macros")))] diff --git a/tests/bot_dialogue.rs b/tests/bot_dialogue.rs new file mode 100644 index 00000000..b3cf3536 --- /dev/null +++ b/tests/bot_dialogue.rs @@ -0,0 +1,64 @@ +#[cfg(feature = "macros")] +use teloxide::macros::DialogueState; +// We put tests here because macro expand in unit tests in the crate was a +// failure + +#[test] +#[cfg(feature = "macros")] +fn compile_test() { + #[allow(dead_code)] + #[derive(DialogueState, Clone)] + #[out(Result<(), teloxide::RequestError>)] + #[store(teloxide::dispatching2::dialogue::InMemStorage)] + enum State { + #[handler(handle_start)] + Start, + + #[handler(handle_have_data)] + HaveData(String), + } + + impl Default for State { + fn default() -> Self { + Self::Start + } + } + + async fn handle_start() -> Result<(), teloxide::RequestError> { + Ok(()) + } + + async fn handle_have_data() -> Result<(), teloxide::RequestError> { + Ok(()) + } +} + +#[test] +#[cfg(feature = "macros")] +fn compile_test_generics() { + #[allow(dead_code)] + #[derive(DialogueState, Clone)] + #[out(Result<(), teloxide::RequestError>)] + #[store(teloxide::dispatching2::dialogue::InMemStorage>)] + enum State { + #[handler(handle_start)] + Start, + + #[handler(handle_have_data)] + HaveData(X), + } + + impl Default for State { + fn default() -> Self { + Self::Start + } + } + + async fn handle_start() -> Result<(), teloxide::RequestError> { + Ok(()) + } + + async fn handle_have_data() -> Result<(), teloxide::RequestError> { + Ok(()) + } +}