diff --git a/examples/dialogue_bot_redis/src/main.rs b/examples/dialogue_bot_redis/src/main.rs index c42aea19..cc8bdd59 100644 --- a/examples/dialogue_bot_redis/src/main.rs +++ b/examples/dialogue_bot_redis/src/main.rs @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; use std::sync::Arc; use teloxide::{ - dispatching::dialogue::{RedisStorage, Serializer, Storage}, + dispatching::dialogue::{serializer::Bincode, RedisStorage, Storage}, prelude::*, types::{KeyboardButton, ReplyKeyboardMarkup}, }; @@ -94,7 +94,7 @@ enum Dialogue { type Cx = DialogueDispatcherHandlerCx< Message, State, - >::Error, + as Storage>::Error, >; type Res = ResponseResult>; @@ -202,7 +202,7 @@ async fn run() { // All serializer but JSON require enabling feature // "serializer-", e. g. "serializer-cbor" // or "serializer-bincode" - RedisStorage::open("redis://127.0.0.1:6379", Serializer::Bincode) + RedisStorage::open("redis://127.0.0.1:6379", Bincode) .await .unwrap(), ), diff --git a/src/dispatching/dialogue/mod.rs b/src/dispatching/dialogue/mod.rs index 99815671..39de77e9 100644 --- a/src/dispatching/dialogue/mod.rs +++ b/src/dispatching/dialogue/mod.rs @@ -56,4 +56,4 @@ pub use dialogue_stage::{exit, next, DialogueStage}; pub use get_chat_id::GetChatId; #[cfg(feature = "redis-storage")] pub use storage::RedisStorage; -pub use storage::{InMemStorage, Serializer, Storage}; +pub use storage::{serializer, InMemStorage, Serializer, Storage}; diff --git a/src/dispatching/dialogue/storage/redis_storage.rs b/src/dispatching/dialogue/storage/redis_storage.rs index b6f45a3a..273902de 100644 --- a/src/dispatching/dialogue/storage/redis_storage.rs +++ b/src/dispatching/dialogue/storage/redis_storage.rs @@ -1,34 +1,37 @@ -use super::{ - serializer::{self, Serializer}, - Storage, -}; +use super::{serializer::Serializer, Storage}; use futures::future::BoxFuture; use redis::{AsyncCommands, FromRedisValue, IntoConnectionInfo}; use serde::{de::DeserializeOwned, Serialize}; -use std::{ops::DerefMut, sync::Arc}; +use std::{ + convert::Infallible, + fmt::{Debug, Display}, + ops::DerefMut, + sync::Arc, +}; use thiserror::Error; use tokio::sync::Mutex; #[derive(Debug, Error)] -pub enum Error { - #[error("{0}")] - SerdeError(#[from] serializer::Error), +pub enum Error +where + SE: Debug + Display, +{ + #[error("parsing/serializing error: {0}")] + SerdeError(SE), #[error("error from Redis: {0}")] RedisError(#[from] redis::RedisError), } -type Result = std::result::Result; - -pub struct RedisStorage { +pub struct RedisStorage { conn: Mutex, - serializer: Serializer, + serializer: S, } -impl RedisStorage { +impl RedisStorage { pub async fn open( url: impl IntoConnectionInfo, - serializer: Serializer, - ) -> Result { + serializer: S, + ) -> Result> { Ok(Self { conn: Mutex::new( redis::Client::open(url)?.get_async_connection().await?, @@ -38,36 +41,42 @@ impl RedisStorage { } } -impl Storage for RedisStorage +impl Storage for RedisStorage where + S: Send + Sync + Serializer + 'static, D: Send + Serialize + DeserializeOwned + 'static, + >::Error: Debug + Display, { - type Error = Error; + type Error = Error<>::Error>; // `.del().ignore()` is much more readable than `.del()\n.ignore()` #[rustfmt::skip] fn remove_dialogue( self: Arc, chat_id: i64, - ) -> BoxFuture<'static, Result>> { + ) -> BoxFuture<'static, Result, Self::Error>> { Box::pin(async move { let res = redis::pipe() .atomic() .get(chat_id) .del(chat_id).ignore() - .query_async::<_, redis::Value>(self.conn.lock().await.deref_mut()) + .query_async::<_, redis::Value>( + self.conn.lock().await.deref_mut(), + ) .await?; - // We're expecting `.pipe()` to return us an exactly one result in bulk, - // so all other branches should be unreachable + // We're expecting `.pipe()` to return us an exactly one result in + // bulk, so all other branches should be unreachable match res { redis::Value::Bulk(bulk) if bulk.len() == 1 => { - Ok( - Option::>::from_redis_value(&bulk[0])? - .map(|v| self.serializer.deserialize(&v)) - .transpose()? - ) - }, - _ => unreachable!() + Ok(Option::>::from_redis_value(&bulk[0])? + .map(|v| { + self.serializer + .deserialize(&v) + .map_err(Error::SerdeError) + }) + .transpose()?) + } + _ => unreachable!(), } }) } @@ -76,16 +85,21 @@ where self: Arc, chat_id: i64, dialogue: D, - ) -> BoxFuture<'static, Result>> { + ) -> BoxFuture<'static, Result, Self::Error>> { Box::pin(async move { - let dialogue = self.serializer.serialize(&dialogue)?; + let dialogue = self + .serializer + .serialize(&dialogue) + .map_err(Error::SerdeError)?; Ok(self .conn .lock() .await .getset::<_, Vec, Option>>(chat_id, dialogue) .await? - .map(|d| self.serializer.deserialize(&d)) + .map(|d| { + self.serializer.deserialize(&d).map_err(Error::SerdeError) + }) .transpose()?) }) } diff --git a/src/dispatching/dialogue/storage/serializer.rs b/src/dispatching/dialogue/storage/serializer.rs index 5ac63a7b..ba4925ab 100644 --- a/src/dispatching/dialogue/storage/serializer.rs +++ b/src/dispatching/dialogue/storage/serializer.rs @@ -1,53 +1,63 @@ use serde::{de::DeserializeOwned, ser::Serialize}; -use thiserror::Error; -use Serializer::*; -#[derive(Debug, Error)] -pub enum Error { - #[error("failed parsing/serializing JSON: {0}")] - JSONError(#[from] serde_json::Error), - #[cfg(feature = "cbor-serializer")] - #[error("failed parsing/serializing CBOR: {0}")] - CBORError(#[from] serde_cbor::Error), - #[cfg(feature = "bincode-serializer")] - #[error("failed parsing/serializing Bincode: {0}")] - BincodeError(#[from] bincode::Error), +pub trait Serializer { + type Error; + + fn serialize(&self, val: &D) -> Result, Self::Error>; + fn deserialize(&self, data: &[u8]) -> Result; } -type Result = std::result::Result; +pub struct JSON; -pub enum Serializer { - JSON, - #[cfg(feature = "cbor-serializer")] - CBOR, - #[cfg(feature = "bincode-serializer")] - Bincode, -} +impl Serializer for JSON +where + D: Serialize + DeserializeOwned, +{ + type Error = serde_json::Error; -impl Serializer { - pub fn serialize(&self, val: &D) -> Result> - where - D: Serialize, - { - Ok(match self { - JSON => serde_json::to_vec(val)?, - #[cfg(feature = "cbor-serializer")] - CBOR => serde_cbor::to_vec(val)?, - #[cfg(feature = "bincode-serializer")] - Bincode => bincode::serialize(val)?, - }) + fn serialize(&self, val: &D) -> Result, Self::Error> { + serde_json::to_vec(val) } - pub fn deserialize<'de, D>(&self, data: &'de [u8]) -> Result - where - D: DeserializeOwned, - { - Ok(match self { - JSON => serde_json::from_slice(data)?, - #[cfg(feature = "cbor-serializer")] - CBOR => serde_cbor::from_slice(data)?, - #[cfg(feature = "bincode-serializer")] - Bincode => bincode::deserialize(data)?, - }) + fn deserialize(&self, data: &[u8]) -> Result { + serde_json::from_slice(data) + } +} + +#[cfg(feature = "cbor-serializer")] +pub struct CBOR; + +#[cfg(feature = "cbor-serializer")] +impl Serializer for CBOR +where + D: Serialize + DeserializeOwned, +{ + type Error = serde_cbor::Error; + + fn serialize(&self, val: &D) -> Result, Self::Error> { + serde_cbor::to_vec(val) + } + + fn deserialize(&self, data: &[u8]) -> Result { + serde_cbor::from_slice(data) + } +} + +#[cfg(feature = "bincode-serializer")] +pub struct Bincode; + +#[cfg(feature = "bincode-serializer")] +impl Serializer for Bincode +where + D: Serialize + DeserializeOwned, +{ + type Error = bincode::Error; + + fn serialize(&self, val: &D) -> Result, Self::Error> { + bincode::serialize(val) + } + + fn deserialize(&self, data: &[u8]) -> Result { + bincode::deserialize(data) } }