From 7a932c8fd99ed94fea567d8b238e265291fa5edf Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sat, 12 Mar 2022 14:33:38 +0600 Subject: [PATCH 1/5] Merge `examples/sqlite_remember.rs` with `redis_remember.rs` --- Cargo.toml | 8 +- .../{sqlite_remember.rs => db_remember.rs} | 27 +--- examples/redis_config.in | 7 ++ examples/redis_remember.rs | 116 ------------------ examples/sqlite_config.in | 7 ++ 5 files changed, 22 insertions(+), 143 deletions(-) rename examples/{sqlite_remember.rs => db_remember.rs} (75%) create mode 100644 examples/redis_config.in delete mode 100644 examples/redis_remember.rs create mode 100644 examples/sqlite_config.in diff --git a/Cargo.toml b/Cargo.toml index a500bb83..71b67886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,17 +128,13 @@ required-features = ["sqlite-storage", "cbor-serializer", "bincode-serializer"] name = "dialogue" required-features = ["macros"] -[[example]] -name = "sqlite_remember" -required-features = ["sqlite-storage", "bincode-serializer", "redis-storage", "macros"] - [[example]] name = "simple_commands" required-features = ["macros"] [[example]] -name = "redis_remember" -required-features = ["redis-storage", "bincode-serializer", "macros"] +name = "db_remember" +required-features = ["sqlite-storage", "redis-storage", "bincode-serializer", "macros"] [[example]] name = "inline" diff --git a/examples/sqlite_remember.rs b/examples/db_remember.rs similarity index 75% rename from examples/sqlite_remember.rs rename to examples/db_remember.rs index e7a7fabd..f53bc9e1 100644 --- a/examples/sqlite_remember.rs +++ b/examples/db_remember.rs @@ -1,24 +1,9 @@ -use teloxide::{ - dispatching2::dialogue::{serializer::Json, SqliteStorage, Storage}, - macros::DialogueState, - prelude2::*, - types::Me, - utils::command::BotCommand, - RequestError, -}; -use thiserror::Error; +use teloxide::{macros::DialogueState, prelude2::*, types::Me, utils::command::BotCommand}; -type MyDialogue = Dialogue>; -type StorageError = as Storage>::Error; +// include!("redis_config.in"); +include!("sqlite_config.in"); -#[derive(Debug, Error)] -enum Error { - #[error("error from Telegram: {0}")] - TelegramError(#[from] RequestError), - - #[error("error from storage: {0}")] - StorageError(#[from] StorageError), -} +type MyDialogue = Dialogue; #[derive(DialogueState, Clone, serde::Serialize, serde::Deserialize)] #[handler_out(anyhow::Result<()>)] @@ -48,10 +33,10 @@ pub enum Command { #[tokio::main] async fn main() { let bot = Bot::from_env().auto_send(); - let storage = SqliteStorage::open("db.sqlite", Json).await.unwrap(); + let storage = open_storage().await; let handler = Update::filter_message() - .enter_dialogue::, State>() + .enter_dialogue::() .dispatch_by::(); Dispatcher::builder(bot, handler) diff --git a/examples/redis_config.in b/examples/redis_config.in new file mode 100644 index 00000000..cd70ebe5 --- /dev/null +++ b/examples/redis_config.in @@ -0,0 +1,7 @@ +use teloxide::dispatching2::dialogue::{RedisStorage, serializer::Bincode}; + +type MyStorage = RedisStorage; + +async fn open_storage() -> std::sync::Arc { + RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap() +} diff --git a/examples/redis_remember.rs b/examples/redis_remember.rs deleted file mode 100644 index 0935bcdf..00000000 --- a/examples/redis_remember.rs +++ /dev/null @@ -1,116 +0,0 @@ -use teloxide::{ - dispatching2::dialogue::{serializer::Bincode, RedisStorage, Storage}, - macros::DialogueState, - prelude2::*, - types::Me, - utils::command::BotCommand, - RequestError, -}; -use thiserror::Error; - -type MyDialogue = Dialogue>; -type StorageError = as Storage>::Error; - -#[derive(Debug, Error)] -enum Error { - #[error("error from Telegram: {0}")] - TelegramError(#[from] RequestError), - - #[error("error from storage: {0}")] - StorageError(#[from] StorageError), -} - -#[derive(DialogueState, Clone, serde::Serialize, serde::Deserialize)] -#[handler_out(anyhow::Result<()>)] -pub enum State { - #[handler(handle_start)] - Start, - - #[handler(handle_got_number)] - GotNumber(i32), -} - -impl Default for State { - fn default() -> Self { - Self::Start - } -} - -#[derive(BotCommand)] -#[command(rename = "lowercase", description = "These commands are supported:")] -pub enum Command { - #[command(description = "get your number.")] - Get, - #[command(description = "reset your number.")] - Reset, -} -#[tokio::main] -async fn main() { - let bot = Bot::from_env().auto_send(); - // You can also choose serializer::JSON or serializer::CBOR - // All serializers but JSON require enabling feature - // "serializer-", e. g. "serializer-cbor" - // or "serializer-bincode" - let storage = RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap(); - - let handler = Update::filter_message() - .enter_dialogue::, State>() - .dispatch_by::(); - - Dispatcher::builder(bot, handler) - .dependencies(dptree::deps![storage]) - .build() - .setup_ctrlc_handler() - .dispatch() - .await; -} - -async fn handle_start( - bot: AutoSend, - msg: Message, - dialogue: MyDialogue, -) -> anyhow::Result<()> { - match msg.text().unwrap().parse() { - Ok(number) => { - dialogue.update(State::GotNumber(number)).await?; - bot.send_message( - msg.chat.id, - format!("Remembered number {}. Now use /get or /reset", number), - ) - .await?; - } - _ => { - bot.send_message(msg.chat.id, "Please, send me a number").await?; - } - } - - Ok(()) -} - -async fn handle_got_number( - bot: AutoSend, - msg: Message, - dialogue: MyDialogue, - num: i32, - me: Me, -) -> anyhow::Result<()> { - let ans = msg.text().unwrap(); - let bot_name = me.user.username.unwrap(); - - match Command::parse(ans, bot_name) { - Ok(cmd) => match cmd { - Command::Get => { - bot.send_message(msg.chat.id, format!("Here is your number: {}", num)).await?; - } - Command::Reset => { - dialogue.reset().await?; - bot.send_message(msg.chat.id, "Number resetted").await?; - } - }, - Err(_) => { - bot.send_message(msg.chat.id, "Please, send /get or /reset").await?; - } - } - - Ok(()) -} diff --git a/examples/sqlite_config.in b/examples/sqlite_config.in new file mode 100644 index 00000000..33d028e3 --- /dev/null +++ b/examples/sqlite_config.in @@ -0,0 +1,7 @@ +use teloxide::dispatching2::dialogue::{SqliteStorage, serializer::Json}; + +type MyStorage = SqliteStorage; + +async fn open_storage() -> std::sync::Arc { + SqliteStorage::open("db.sqlite", Json).await.unwrap() +} From a8fbee59147a79f44996267f1ac2ce22d5f31da4 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sat, 12 Mar 2022 14:36:31 +0600 Subject: [PATCH 2/5] Simplify the example usage guide --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index c3081bfc..09841a1c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,7 +1,7 @@ # Usage ``` -$ cargo run --example --features="" +$ cargo run --features="full" --example ``` Don't forget to initialise the `TELOXIDE_TOKEN` environmental variable. From cf8c1b78b735753a069bfdb6aebf1290da367f3e Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Mon, 21 Mar 2022 20:06:32 +0600 Subject: [PATCH 3/5] Rewrite `examples/db_remember.rs` to use `ErasedStorage` --- CHANGELOG.md | 3 +- examples/db_remember.rs | 38 ++++++---- examples/redis_config.in | 7 -- examples/sqlite_config.in | 7 -- src/dispatching/dialogue/storage/mod.rs | 97 ++++++++++++------------- src/dispatching2/dialogue/mod.rs | 12 ++- src/dispatching2/handler_ext.rs | 4 +- 7 files changed, 84 insertions(+), 84 deletions(-) delete mode 100644 examples/redis_config.in delete mode 100644 examples/sqlite_config.in diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b3cc0a2..b2d33a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- The `Storage::erase` default function that returns `ErasedStorage`. +- The `Storage::erase` default function that returns `Arc`. - `ErasedStorage`, a storage with an erased error type. +- Allow the storage generic `S` be `?Sized` in `Dialogue`. ## 0.7.1 - 2022-03-09 diff --git a/examples/db_remember.rs b/examples/db_remember.rs index f53bc9e1..65fd1f94 100644 --- a/examples/db_remember.rs +++ b/examples/db_remember.rs @@ -1,12 +1,23 @@ -use teloxide::{macros::DialogueState, prelude2::*, types::Me, utils::command::BotCommand}; +// Set the `DB_REMEMBER_REDIS` environmental variable if you want to use Redis. +// Otherwise, the default is Sqlite. -// include!("redis_config.in"); -include!("sqlite_config.in"); +use teloxide::{ + dispatching2::dialogue::{ + serializer::{Bincode, Json}, + ErasedStorage, RedisStorage, SqliteStorage, Storage, + }, + macros::DialogueState, + prelude2::*, + types::Me, + utils::command::BotCommand, +}; -type MyDialogue = Dialogue; +type MyDialogue = Dialogue>; +type MyStorage = std::sync::Arc>; +type HandlerResult = Result<(), Box>; #[derive(DialogueState, Clone, serde::Serialize, serde::Deserialize)] -#[handler_out(anyhow::Result<()>)] +#[handler_out(HandlerResult)] pub enum State { #[handler(handle_start)] Start, @@ -34,9 +45,14 @@ pub enum Command { async fn main() { let bot = Bot::from_env().auto_send(); - let storage = open_storage().await; + let storage: MyStorage = if std::env::var("DB_REMEMBER_REDIS").is_ok() { + RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap().erase() + } else { + SqliteStorage::open("db.sqlite", Json).await.unwrap().erase() + }; + let handler = Update::filter_message() - .enter_dialogue::() + .enter_dialogue::, State>() .dispatch_by::(); Dispatcher::builder(bot, handler) @@ -47,11 +63,7 @@ async fn main() { .await; } -async fn handle_start( - bot: AutoSend, - msg: Message, - dialogue: MyDialogue, -) -> anyhow::Result<()> { +async fn handle_start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { match msg.text().unwrap().parse() { Ok(number) => { dialogue.update(State::GotNumber(number)).await?; @@ -75,7 +87,7 @@ async fn handle_got_number( dialogue: MyDialogue, num: i32, me: Me, -) -> anyhow::Result<()> { +) -> HandlerResult { let ans = msg.text().unwrap(); let bot_name = me.user.username.unwrap(); diff --git a/examples/redis_config.in b/examples/redis_config.in deleted file mode 100644 index cd70ebe5..00000000 --- a/examples/redis_config.in +++ /dev/null @@ -1,7 +0,0 @@ -use teloxide::dispatching2::dialogue::{RedisStorage, serializer::Bincode}; - -type MyStorage = RedisStorage; - -async fn open_storage() -> std::sync::Arc { - RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap() -} diff --git a/examples/sqlite_config.in b/examples/sqlite_config.in deleted file mode 100644 index 33d028e3..00000000 --- a/examples/sqlite_config.in +++ /dev/null @@ -1,7 +0,0 @@ -use teloxide::dispatching2::dialogue::{SqliteStorage, serializer::Json}; - -type MyStorage = SqliteStorage; - -async fn open_storage() -> std::sync::Arc { - SqliteStorage::open("db.sqlite", Json).await.unwrap() -} diff --git a/src/dispatching/dialogue/storage/mod.rs b/src/dispatching/dialogue/storage/mod.rs index 93dec439..bb8463cb 100644 --- a/src/dispatching/dialogue/storage/mod.rs +++ b/src/dispatching/dialogue/storage/mod.rs @@ -74,64 +74,59 @@ pub trait Storage { ) -> BoxFuture<'static, Result, Self::Error>>; /// Erases [`Self::Error`] to [`std::error::Error`]. - fn erase(self: Arc) -> ErasedStorage + #[must_use] + fn erase(self: Arc) -> Arc> where Self: Sized + Send + Sync + 'static, - Self::Error: std::error::Error + 'static, + Self::Error: std::error::Error + Send + Sync + 'static, { - struct Eraser(Arc); - - impl Storage for Eraser - where - S: Storage + Send + Sync + 'static, - S::Error: std::error::Error + 'static, - { - type Error = Box; - - fn remove_dialogue( - self: Arc, - chat_id: i64, - ) -> BoxFuture<'static, Result<(), Self::Error>> - where - D: Send + 'static, - { - Box::pin(async move { - Arc::clone(&self.0).remove_dialogue(chat_id).await.map_err(|e| e.into()) - }) - } - - fn update_dialogue( - self: Arc, - chat_id: i64, - dialogue: D, - ) -> BoxFuture<'static, Result<(), Self::Error>> - where - D: Send + 'static, - { - Box::pin(async move { - Arc::clone(&self.0) - .update_dialogue(chat_id, dialogue) - .await - .map_err(|e| e.into()) - }) - } - - fn get_dialogue( - self: Arc, - chat_id: i64, - ) -> BoxFuture<'static, Result, Self::Error>> { - Box::pin(async move { - Arc::clone(&self.0).get_dialogue(chat_id).await.map_err(|e| e.into()) - }) - } - } - Arc::new(Eraser(self)) } } -/// A storage with an erased error type. -pub type ErasedStorage = Arc>>; +struct Eraser(Arc); + +impl Storage for Eraser +where + S: Storage + Send + Sync + 'static, + S::Error: std::error::Error + Send + Sync + 'static, +{ + type Error = Box; + + fn remove_dialogue(self: Arc, chat_id: i64) -> BoxFuture<'static, Result<(), Self::Error>> + where + D: Send + 'static, + { + Box::pin( + async move { Arc::clone(&self.0).remove_dialogue(chat_id).await.map_err(|e| e.into()) }, + ) + } + + fn update_dialogue( + self: Arc, + chat_id: i64, + dialogue: D, + ) -> BoxFuture<'static, Result<(), Self::Error>> + where + D: Send + 'static, + { + Box::pin(async move { + Arc::clone(&self.0).update_dialogue(chat_id, dialogue).await.map_err(|e| e.into()) + }) + } + + fn get_dialogue( + self: Arc, + chat_id: i64, + ) -> BoxFuture<'static, Result, Self::Error>> { + Box::pin( + async move { Arc::clone(&self.0).get_dialogue(chat_id).await.map_err(|e| e.into()) }, + ) + } +} + +pub type ErasedStorage = + dyn Storage> + Send + Sync; #[cfg(test)] mod tests { diff --git a/src/dispatching2/dialogue/mod.rs b/src/dispatching2/dialogue/mod.rs index 1dbdc279..ae5dc020 100644 --- a/src/dispatching2/dialogue/mod.rs +++ b/src/dispatching2/dialogue/mod.rs @@ -102,7 +102,10 @@ mod get_chat_id; /// A handle for controlling dialogue state. #[derive(Debug)] -pub struct Dialogue { +pub struct Dialogue +where + S: ?Sized, +{ storage: Arc, chat_id: i64, _phantom: PhantomData, @@ -110,7 +113,10 @@ pub struct Dialogue { // `#[derive]` requires generics to implement `Clone`, but `S` is wrapped around // `Arc`, and `D` is wrapped around PhantomData. -impl Clone for Dialogue { +impl Clone for Dialogue +where + S: ?Sized, +{ fn clone(&self) -> Self { Dialogue { storage: self.storage.clone(), chat_id: self.chat_id, _phantom: PhantomData } } @@ -119,7 +125,7 @@ impl Clone for Dialogue { impl Dialogue where D: Send + 'static, - S: Storage, + S: Storage + ?Sized, { /// Constructs a new dialogue with `storage` (where dialogues are stored) /// and `chat_id` of a current dialogue. diff --git a/src/dispatching2/handler_ext.rs b/src/dispatching2/handler_ext.rs index 7c3db7ec..6fcafca1 100644 --- a/src/dispatching2/handler_ext.rs +++ b/src/dispatching2/handler_ext.rs @@ -45,7 +45,7 @@ pub trait HandlerExt { #[must_use] fn enter_dialogue(self) -> Self where - S: Storage + Send + Sync + 'static, + S: Storage + ?Sized + Send + Sync + 'static, >::Error: Debug + Send, D: Default + Send + Sync + 'static, Upd: GetChatId + Clone + Send + Sync + 'static; @@ -72,7 +72,7 @@ where fn enter_dialogue(self) -> Self where - S: Storage + Send + Sync + 'static, + S: Storage + ?Sized + Send + Sync + 'static, >::Error: Debug + Send, D: Default + Send + Sync + 'static, Upd: GetChatId + Clone + Send + Sync + 'static, From 7a7667fff9217dd5950d49bf4ddf13673925f62f Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Mon, 21 Mar 2022 20:09:06 +0600 Subject: [PATCH 4/5] Changelogify `?Sized` in `HandlerExt::enter_dialogue` --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2d33a7f..17957b1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `Storage::erase` default function that returns `Arc`. - `ErasedStorage`, a storage with an erased error type. -- Allow the storage generic `S` be `?Sized` in `Dialogue`. +- Allow the storage generic `S` be `?Sized` in `Dialogue` and `HandlerExt::enter_dialogue`. ## 0.7.1 - 2022-03-09 From be143a3f6ecb0d72d8ca10e71187bff97ed4faf1 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Mon, 21 Mar 2022 20:18:33 +0600 Subject: [PATCH 5/5] Move `ErasedStorage` upper --- src/dispatching/dialogue/storage/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dispatching/dialogue/storage/mod.rs b/src/dispatching/dialogue/storage/mod.rs index bb8463cb..600cf988 100644 --- a/src/dispatching/dialogue/storage/mod.rs +++ b/src/dispatching/dialogue/storage/mod.rs @@ -24,6 +24,10 @@ use std::sync::Arc; #[cfg(feature = "sqlite-storage")] pub use sqlite_storage::{SqliteStorage, SqliteStorageError}; +/// A storage with an erased error type. +pub type ErasedStorage = + dyn Storage> + Send + Sync; + /// A storage of dialogues. /// /// You can implement this trait for a structure that communicates with a DB and @@ -125,9 +129,6 @@ where } } -pub type ErasedStorage = - dyn Storage> + Send + Sync; - #[cfg(test)] mod tests { use super::*;