forked from teloxide/teloxide
-
Notifications
You must be signed in to change notification settings - Fork 0
/
storage.rs
158 lines (135 loc) · 4.65 KB
/
storage.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
pub mod serializer;
mod in_mem_storage;
mod trace_storage;
#[cfg(feature = "redis-storage")]
mod redis_storage;
#[cfg(feature = "sqlite-storage")]
mod sqlite_storage;
#[cfg(feature = "rocksdb-storage")]
mod rocksdb_storage;
use futures::future::BoxFuture;
use teloxide_core::types::ChatId;
pub use self::{
in_mem_storage::{InMemStorage, InMemStorageError},
trace_storage::TraceStorage,
};
#[cfg(feature = "redis-storage")]
pub use redis_storage::{RedisStorage, RedisStorageError};
pub use serializer::Serializer;
use std::sync::Arc;
#[cfg(feature = "sqlite-storage")]
pub use sqlite_storage::{SqliteStorage, SqliteStorageError};
#[cfg(feature = "rocksdb-storage")]
pub use rocksdb_storage::{RocksDbStorage, RocksDbStorageError};
/// A storage with an erased error type.
pub type ErasedStorage<D> =
dyn Storage<D, Error = Box<dyn std::error::Error + Send + Sync>> + Send + Sync;
/// A storage of dialogues.
///
/// You can implement this trait for a structure that communicates with a DB and
/// be sure that after you restart your bot, all the dialogues won't be lost.
///
/// `Storage` is used only to store dialogue states, i.e. it can't be used as a
/// generic database.
///
/// Currently we support the following storages out of the box:
///
/// - [`InMemStorage`] -- a storage based on [`std::collections::HashMap`].
/// - [`RedisStorage`] -- a Redis-based storage.
/// - [`RocksDbStorage`] -- a RocksDB-based persistent storage.
/// - [`SqliteStorage`] -- an SQLite-based persistent storage.
///
/// [`InMemStorage`]: crate::dispatching::dialogue::InMemStorage
/// [`RedisStorage`]: crate::dispatching::dialogue::RedisStorage
/// [`RocksDbStorage`]: crate::dispatching::dialogue::RocksDbStorage
/// [`SqliteStorage`]: crate::dispatching::dialogue::SqliteStorage
pub trait Storage<D> {
type Error;
/// Removes a dialogue indexed by `chat_id`.
///
/// If the dialogue indexed by `chat_id` does not exist, this function
/// results in an error.
#[must_use = "Futures are lazy and do nothing unless polled with .await"]
fn remove_dialogue(
self: Arc<Self>,
chat_id: ChatId,
) -> BoxFuture<'static, Result<(), Self::Error>>
where
D: Send + 'static;
/// Updates a dialogue indexed by `chat_id` with `dialogue`.
#[must_use = "Futures are lazy and do nothing unless polled with .await"]
fn update_dialogue(
self: Arc<Self>,
chat_id: ChatId,
dialogue: D,
) -> BoxFuture<'static, Result<(), Self::Error>>
where
D: Send + 'static;
/// Returns the dialogue indexed by `chat_id`.
#[must_use = "Futures are lazy and do nothing unless polled with .await"]
fn get_dialogue(
self: Arc<Self>,
chat_id: ChatId,
) -> BoxFuture<'static, Result<Option<D>, Self::Error>>;
/// Erases [`Self::Error`] to [`std::error::Error`].
#[must_use]
fn erase(self: Arc<Self>) -> Arc<ErasedStorage<D>>
where
Self: Sized + Send + Sync + 'static,
Self::Error: std::error::Error + Send + Sync + 'static,
{
Arc::new(Eraser(self))
}
}
struct Eraser<S>(Arc<S>);
impl<D, S> Storage<D> for Eraser<S>
where
S: Storage<D> + Send + Sync + 'static,
S::Error: std::error::Error + Send + Sync + 'static,
{
type Error = Box<dyn std::error::Error + Send + Sync>;
fn remove_dialogue(
self: Arc<Self>,
chat_id: ChatId,
) -> 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<Self>,
chat_id: ChatId,
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<Self>,
chat_id: ChatId,
) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
Box::pin(
async move { Arc::clone(&self.0).get_dialogue(chat_id).await.map_err(|e| e.into()) },
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_erased() {
let chat_id = ChatId(123);
let erased = InMemStorage::new().erase();
Arc::clone(&erased).update_dialogue(chat_id, 1).await.unwrap();
assert_eq!(Arc::clone(&erased).get_dialogue(chat_id).await.unwrap().unwrap(), 1);
Arc::clone(&erased).remove_dialogue(chat_id).await.unwrap();
assert_eq!(Arc::clone(&erased).get_dialogue(chat_id).await.unwrap(), None);
}
}