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,