1use crate::common::{Storage, StorageMode};
2use std::fmt;
3
4pub trait StorageFactory {
6 fn create_storage(&self) -> Result<Box<dyn Storage>, StorageError>;
7}
8
9#[derive(Debug)]
11pub enum StorageError {
12 ConnectionFailed(String),
13 InvalidConfiguration(String),
14 IoError(String),
15}
16
17impl fmt::Display for StorageError {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 StorageError::ConnectionFailed(msg) => write!(f, "Connection failed: {}", msg),
21 StorageError::InvalidConfiguration(msg) => write!(f, "Invalid configuration: {}", msg),
22 StorageError::IoError(msg) => write!(f, "IO error: {}", msg),
23 }
24 }
25}
26
27impl std::error::Error for StorageError {}
28
29#[derive(Debug, Clone)]
31pub enum StorageConfig {
32 Memory,
33 Lmdb {
34 path: String,
35 mode: StorageMode,
36 max_read_counter_reopen: Option<u64>,
37 },
38 Remote {
39 address: String,
40 },
41 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
42 Tarantool {
43 uri: String,
44 login: String,
45 password: String,
46 },
47}
48
49pub struct StorageBuilder {
51 config: Option<StorageConfig>,
52}
53
54impl StorageBuilder {
55 pub fn new() -> Self {
56 Self { config: None }
57 }
58
59 pub fn memory(mut self) -> Self {
60 self.config = Some(StorageConfig::Memory);
61 self
62 }
63
64 pub fn lmdb(mut self, path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> Self {
65 self.config = Some(StorageConfig::Lmdb {
66 path: path.to_string(),
67 mode,
68 max_read_counter_reopen,
69 });
70 self
71 }
72
73 pub fn remote(mut self, address: &str) -> Self {
74 self.config = Some(StorageConfig::Remote {
75 address: address.to_string(),
76 });
77 self
78 }
79
80 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
81 pub fn tarantool(mut self, uri: &str, login: &str, password: &str) -> Self {
82 self.config = Some(StorageConfig::Tarantool {
83 uri: uri.to_string(),
84 login: login.to_string(),
85 password: password.to_string(),
86 });
87 self
88 }
89
90 pub fn build(self) -> Result<Box<dyn Storage>, StorageError> {
91 let config = self.config.ok_or_else(|| {
92 StorageError::InvalidConfiguration("No storage type specified".to_string())
93 })?;
94
95 DefaultStorageFactory::new().create_storage_from_config(config)
96 }
97
98 pub fn build_memory_generic(self) -> Result<crate::vstorage::VMemoryStorage, StorageError> {
104 if let Some(StorageConfig::Memory) = self.config {
105 Ok(crate::vstorage::VMemoryStorage::new(crate::memory_storage::MemoryStorage::new()))
106 } else {
107 Err(StorageError::InvalidConfiguration(
108 "Builder is not configured for memory storage".to_string()
109 ))
110 }
111 }
112
113 pub fn build_lmdb_generic(self) -> Result<crate::vstorage::VLMDBStorage, StorageError> {
115 if let Some(StorageConfig::Lmdb { path, mode, max_read_counter_reopen }) = self.config {
116 Ok(crate::vstorage::VLMDBStorage::new(crate::lmdb_storage::LMDBStorage::new(&path, mode, max_read_counter_reopen)))
117 } else {
118 Err(StorageError::InvalidConfiguration(
119 "Builder is not configured for LMDB storage".to_string()
120 ))
121 }
122 }
123
124 pub fn build_remote_generic(self) -> Result<crate::vstorage::VRemoteStorage, StorageError> {
126 if let Some(StorageConfig::Remote { address }) = self.config {
127 Ok(crate::vstorage::VRemoteStorage::new(crate::remote_storage_client::StorageROClient::new(&address)))
128 } else {
129 Err(StorageError::InvalidConfiguration(
130 "Builder is not configured for remote storage".to_string()
131 ))
132 }
133 }
134
135 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
137 pub fn build_tarantool_generic(self) -> Result<crate::vstorage::VTTStorage, StorageError> {
138 if let Some(StorageConfig::Tarantool { uri, login, password }) = self.config {
139 Ok(crate::vstorage::VTTStorage::new(crate::tt_storage::TTStorage::new(uri, &login, &password)))
140 } else {
141 Err(StorageError::InvalidConfiguration(
142 "Builder is not configured for Tarantool storage".to_string()
143 ))
144 }
145 }
146}
147
148impl Default for StorageBuilder {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154pub struct StorageProvider;
165
166impl StorageProvider {
167 pub fn memory() -> Box<dyn Storage> {
169 log::info!("Creating in-memory storage");
170 Box::new(crate::memory_storage::MemoryStorage::new())
171 }
172
173 pub fn lmdb(db_path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> Box<dyn Storage> {
175 log::info!("Trying to connect to [LMDB], path: {}", db_path);
176 Box::new(crate::lmdb_storage::LMDBStorage::new(db_path, mode, max_read_counter_reopen))
177 }
178
179 pub fn remote(addr: &str) -> Box<dyn Storage> {
181 log::info!("Trying to connect to [remote], addr: {}", addr);
182 Box::new(crate::remote_storage_client::StorageROClient::new(addr))
183 }
184
185 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
187 pub fn tarantool(tt_uri: String, login: &str, pass: &str) -> Box<dyn Storage> {
188 log::info!("Trying to connect to [Tarantool], addr: {}", tt_uri);
189 Box::new(crate::tt_storage::TTStorage::new(tt_uri, login, pass))
190 }
191
192 pub fn vstorage_memory() -> crate::vstorage::VStorage {
194 crate::vstorage::VStorage::new(Self::memory())
195 }
196
197 pub fn vstorage_lmdb(db_path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> crate::vstorage::VStorage {
199 crate::vstorage::VStorage::new(Self::lmdb(db_path, mode, max_read_counter_reopen))
200 }
201
202 pub fn vstorage_remote(addr: &str) -> crate::vstorage::VStorage {
204 crate::vstorage::VStorage::new(Self::remote(addr))
205 }
206
207 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
209 pub fn vstorage_tarantool(tt_uri: String, login: &str, pass: &str) -> crate::vstorage::VStorage {
210 crate::vstorage::VStorage::new(Self::tarantool(tt_uri, login, pass))
211 }
212
213 pub fn memory_generic() -> crate::vstorage::VMemoryStorage {
219 log::info!("Creating generic in-memory storage");
220 crate::vstorage::VMemoryStorage::new(crate::memory_storage::MemoryStorage::new())
221 }
222
223 pub fn lmdb_generic(db_path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> crate::vstorage::VLMDBStorage {
225 log::info!("Creating generic LMDB storage, path: {}", db_path);
226 crate::vstorage::VLMDBStorage::new(crate::lmdb_storage::LMDBStorage::new(db_path, mode, max_read_counter_reopen))
227 }
228
229 pub fn remote_generic(addr: &str) -> crate::vstorage::VRemoteStorage {
231 log::info!("Creating generic remote storage, addr: {}", addr);
232 crate::vstorage::VRemoteStorage::new(crate::remote_storage_client::StorageROClient::new(addr))
233 }
234
235 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
237 pub fn tarantool_generic(tt_uri: String, login: &str, pass: &str) -> crate::vstorage::VTTStorage {
238 log::info!("Creating generic Tarantool storage, addr: {}", tt_uri);
239 crate::vstorage::VTTStorage::new(crate::tt_storage::TTStorage::new(tt_uri, login, pass))
240 }
241}
242
243pub struct DefaultStorageFactory;
245
246impl DefaultStorageFactory {
247 pub fn new() -> Self {
248 Self
249 }
250
251 pub fn create_storage_from_config(&self, config: StorageConfig) -> Result<Box<dyn Storage>, StorageError> {
252 match config {
253 StorageConfig::Memory => {
254 Ok(StorageProvider::memory())
255 }
256 StorageConfig::Lmdb { path, mode, max_read_counter_reopen } => {
257 Ok(StorageProvider::lmdb(&path, mode, max_read_counter_reopen))
258 }
259 StorageConfig::Remote { address } => {
260 Ok(StorageProvider::remote(&address))
261 }
262 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
263 StorageConfig::Tarantool { uri, login, password } => {
264 Ok(StorageProvider::tarantool(uri, &login, &password))
265 }
266 }
267 }
268}
269
270impl Default for DefaultStorageFactory {
271 fn default() -> Self {
272 Self::new()
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn test_storage_builder_memory() {
282 let storage = StorageBuilder::new()
283 .memory()
284 .build();
285
286 assert!(storage.is_ok());
287 }
288
289 #[test]
290 fn test_storage_builder_no_config() {
291 let storage = StorageBuilder::new().build();
292 assert!(storage.is_err());
293 }
294
295 #[test]
296 fn test_generic_memory_builder() {
297 let storage = StorageBuilder::new()
298 .memory()
299 .build_memory_generic();
300
301 assert!(storage.is_ok());
302 }
303
304 #[test]
305 fn test_generic_lmdb_builder() {
306 let storage = StorageBuilder::new()
307 .lmdb("/tmp/test", StorageMode::ReadOnly, None)
308 .build_lmdb_generic();
309
310 assert!(storage.is_ok());
311 }
312
313 #[test]
314 fn test_generic_remote_builder() {
315 let storage = StorageBuilder::new()
316 .remote("127.0.0.1:8080")
317 .build_remote_generic();
318
319 assert!(storage.is_ok());
320 }
321
322 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
323 #[test]
324 fn test_generic_tarantool_builder() {
325 let storage = StorageBuilder::new()
326 .tarantool("127.0.0.1:3301", "user", "pass")
327 .build_tarantool_generic();
328
329 assert!(storage.is_ok());
330 }
331
332 #[test]
333 fn test_generic_builder_wrong_config() {
334 let storage = StorageBuilder::new()
335 .lmdb("/tmp/test", StorageMode::ReadOnly, None)
336 .build_memory_generic();
337
338 assert!(storage.is_err());
339 }
340
341 #[test]
346 fn test_storage_provider_memory() {
347 let _storage = StorageProvider::memory();
348 }
350
351 #[test]
352 fn test_storage_provider_vstorage_memory() {
353 let mut storage = StorageProvider::vstorage_memory();
354 assert!(!storage.is_empty());
355
356 assert!(storage.put_value(crate::common::StorageId::Individuals, "test", "value").is_ok());
358 let get_result = storage.get_value(crate::common::StorageId::Individuals, "test");
359 assert!(get_result.is_ok(), "Expected Ok, got: {:?}", get_result);
360 if let crate::common::StorageResult::Ok(value) = get_result {
361 assert_eq!(value, "value");
362 }
363 }
364
365 #[test]
366 fn test_storage_provider_generic_memory() {
367 let mut storage = StorageProvider::memory_generic();
368 assert!(!storage.is_empty());
369
370 assert!(storage.put_value(crate::common::StorageId::Individuals, "test", "value").is_ok());
372 let get_result = storage.get_value(crate::common::StorageId::Individuals, "test");
373 assert!(get_result.is_ok(), "Expected Ok, got: {:?}", get_result);
374 if let crate::common::StorageResult::Ok(value) = get_result {
375 assert_eq!(value, "value");
376 }
377 }
378
379 #[test]
380 fn test_storage_provider_lmdb() {
381 let _storage = StorageProvider::lmdb("/tmp/test", StorageMode::ReadOnly, None);
382 }
384
385 #[test]
386 fn test_storage_provider_remote() {
387 let _storage = StorageProvider::remote("127.0.0.1:8080");
388 }
390
391 #[cfg(any(feature = "tt_2", feature = "tt_3"))]
392 #[test]
393 fn test_storage_provider_tarantool() {
394 let _storage = StorageProvider::tarantool("127.0.0.1:3301".to_string(), "user", "pass");
395 }
397}