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 Tarantool {
42 uri: String,
43 login: String,
44 password: String,
45 },
46}
47
48pub struct StorageBuilder {
50 config: Option<StorageConfig>,
51}
52
53impl StorageBuilder {
54 pub fn new() -> Self {
55 Self { config: None }
56 }
57
58 pub fn memory(mut self) -> Self {
59 self.config = Some(StorageConfig::Memory);
60 self
61 }
62
63 pub fn lmdb(mut self, path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> Self {
64 self.config = Some(StorageConfig::Lmdb {
65 path: path.to_string(),
66 mode,
67 max_read_counter_reopen,
68 });
69 self
70 }
71
72 pub fn remote(mut self, address: &str) -> Self {
73 self.config = Some(StorageConfig::Remote {
74 address: address.to_string(),
75 });
76 self
77 }
78
79 pub fn tarantool(mut self, uri: &str, login: &str, password: &str) -> Self {
80 self.config = Some(StorageConfig::Tarantool {
81 uri: uri.to_string(),
82 login: login.to_string(),
83 password: password.to_string(),
84 });
85 self
86 }
87
88 pub fn build(self) -> Result<Box<dyn Storage>, StorageError> {
89 let config = self.config.ok_or_else(|| {
90 StorageError::InvalidConfiguration("No storage type specified".to_string())
91 })?;
92
93 DefaultStorageFactory::new().create_storage_from_config(config)
94 }
95
96 pub fn build_memory_generic(self) -> Result<crate::vstorage::VMemoryStorage, StorageError> {
102 if let Some(StorageConfig::Memory) = self.config {
103 Ok(crate::vstorage::VMemoryStorage::new(crate::memory_storage::MemoryStorage::new()))
104 } else {
105 Err(StorageError::InvalidConfiguration(
106 "Builder is not configured for memory storage".to_string()
107 ))
108 }
109 }
110
111 pub fn build_lmdb_generic(self) -> Result<crate::vstorage::VLMDBStorage, StorageError> {
113 if let Some(StorageConfig::Lmdb { path, mode, max_read_counter_reopen }) = self.config {
114 Ok(crate::vstorage::VLMDBStorage::new(crate::lmdb_storage::LMDBStorage::new(&path, mode, max_read_counter_reopen)))
115 } else {
116 Err(StorageError::InvalidConfiguration(
117 "Builder is not configured for LMDB storage".to_string()
118 ))
119 }
120 }
121
122 pub fn build_remote_generic(self) -> Result<crate::vstorage::VRemoteStorage, StorageError> {
124 if let Some(StorageConfig::Remote { address }) = self.config {
125 Ok(crate::vstorage::VRemoteStorage::new(crate::remote_storage_client::StorageROClient::new(&address)))
126 } else {
127 Err(StorageError::InvalidConfiguration(
128 "Builder is not configured for remote storage".to_string()
129 ))
130 }
131 }
132
133 pub fn build_tarantool_generic(self) -> Result<crate::vstorage::VTTStorage, StorageError> {
135 if let Some(StorageConfig::Tarantool { uri, login, password }) = self.config {
136 Ok(crate::vstorage::VTTStorage::new(crate::tt_storage::TTStorage::new(uri, &login, &password)))
137 } else {
138 Err(StorageError::InvalidConfiguration(
139 "Builder is not configured for Tarantool storage".to_string()
140 ))
141 }
142 }
143}
144
145impl Default for StorageBuilder {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151pub struct StorageProvider;
162
163impl StorageProvider {
164 pub fn memory() -> Box<dyn Storage> {
166 log::info!("Creating in-memory storage");
167 Box::new(crate::memory_storage::MemoryStorage::new())
168 }
169
170 pub fn lmdb(db_path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> Box<dyn Storage> {
172 log::info!("Trying to connect to [LMDB], path: {}", db_path);
173 Box::new(crate::lmdb_storage::LMDBStorage::new(db_path, mode, max_read_counter_reopen))
174 }
175
176 pub fn remote(addr: &str) -> Box<dyn Storage> {
178 log::info!("Trying to connect to [remote], addr: {}", addr);
179 Box::new(crate::remote_storage_client::StorageROClient::new(addr))
180 }
181
182 pub fn tarantool(tt_uri: String, login: &str, pass: &str) -> Box<dyn Storage> {
184 log::info!("Trying to connect to [Tarantool], addr: {}", tt_uri);
185 Box::new(crate::tt_storage::TTStorage::new(tt_uri, login, pass))
186 }
187
188 pub fn vstorage_memory() -> crate::vstorage::VStorage {
190 crate::vstorage::VStorage::new(Self::memory())
191 }
192
193 pub fn vstorage_lmdb(db_path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> crate::vstorage::VStorage {
195 crate::vstorage::VStorage::new(Self::lmdb(db_path, mode, max_read_counter_reopen))
196 }
197
198 pub fn vstorage_remote(addr: &str) -> crate::vstorage::VStorage {
200 crate::vstorage::VStorage::new(Self::remote(addr))
201 }
202
203 pub fn vstorage_tarantool(tt_uri: String, login: &str, pass: &str) -> crate::vstorage::VStorage {
205 crate::vstorage::VStorage::new(Self::tarantool(tt_uri, login, pass))
206 }
207
208 pub fn memory_generic() -> crate::vstorage::VMemoryStorage {
214 log::info!("Creating generic in-memory storage");
215 crate::vstorage::VMemoryStorage::new(crate::memory_storage::MemoryStorage::new())
216 }
217
218 pub fn lmdb_generic(db_path: &str, mode: StorageMode, max_read_counter_reopen: Option<u64>) -> crate::vstorage::VLMDBStorage {
220 log::info!("Creating generic LMDB storage, path: {}", db_path);
221 crate::vstorage::VLMDBStorage::new(crate::lmdb_storage::LMDBStorage::new(db_path, mode, max_read_counter_reopen))
222 }
223
224 pub fn remote_generic(addr: &str) -> crate::vstorage::VRemoteStorage {
226 log::info!("Creating generic remote storage, addr: {}", addr);
227 crate::vstorage::VRemoteStorage::new(crate::remote_storage_client::StorageROClient::new(addr))
228 }
229
230 pub fn tarantool_generic(tt_uri: String, login: &str, pass: &str) -> crate::vstorage::VTTStorage {
232 log::info!("Creating generic Tarantool storage, addr: {}", tt_uri);
233 crate::vstorage::VTTStorage::new(crate::tt_storage::TTStorage::new(tt_uri, login, pass))
234 }
235}
236
237pub struct DefaultStorageFactory;
239
240impl DefaultStorageFactory {
241 pub fn new() -> Self {
242 Self
243 }
244
245 pub fn create_storage_from_config(&self, config: StorageConfig) -> Result<Box<dyn Storage>, StorageError> {
246 match config {
247 StorageConfig::Memory => {
248 Ok(StorageProvider::memory())
249 }
250 StorageConfig::Lmdb { path, mode, max_read_counter_reopen } => {
251 Ok(StorageProvider::lmdb(&path, mode, max_read_counter_reopen))
252 }
253 StorageConfig::Remote { address } => {
254 Ok(StorageProvider::remote(&address))
255 }
256 StorageConfig::Tarantool { uri, login, password } => {
257 Ok(StorageProvider::tarantool(uri, &login, &password))
258 }
259 }
260 }
261}
262
263impl Default for DefaultStorageFactory {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_storage_builder_memory() {
275 let storage = StorageBuilder::new()
276 .memory()
277 .build();
278
279 assert!(storage.is_ok());
280 }
281
282 #[test]
283 fn test_storage_builder_no_config() {
284 let storage = StorageBuilder::new().build();
285 assert!(storage.is_err());
286 }
287
288 #[test]
289 fn test_generic_memory_builder() {
290 let storage = StorageBuilder::new()
291 .memory()
292 .build_memory_generic();
293
294 assert!(storage.is_ok());
295 }
296
297 #[test]
298 fn test_generic_lmdb_builder() {
299 let storage = StorageBuilder::new()
300 .lmdb("/tmp/test", StorageMode::ReadOnly, None)
301 .build_lmdb_generic();
302
303 assert!(storage.is_ok());
304 }
305
306 #[test]
307 fn test_generic_remote_builder() {
308 let storage = StorageBuilder::new()
309 .remote("127.0.0.1:8080")
310 .build_remote_generic();
311
312 assert!(storage.is_ok());
313 }
314
315 #[test]
316 fn test_generic_tarantool_builder() {
317 let storage = StorageBuilder::new()
318 .tarantool("127.0.0.1:3301", "user", "pass")
319 .build_tarantool_generic();
320
321 assert!(storage.is_ok());
322 }
323
324 #[test]
325 fn test_generic_builder_wrong_config() {
326 let storage = StorageBuilder::new()
327 .lmdb("/tmp/test", StorageMode::ReadOnly, None)
328 .build_memory_generic();
329
330 assert!(storage.is_err());
331 }
332
333 #[test]
338 fn test_storage_provider_memory() {
339 let _storage = StorageProvider::memory();
340 }
342
343 #[test]
344 fn test_storage_provider_vstorage_memory() {
345 let mut storage = StorageProvider::vstorage_memory();
346 assert!(!storage.is_empty());
347
348 assert!(storage.put_value(crate::common::StorageId::Individuals, "test", "value").is_ok());
350 let get_result = storage.get_value(crate::common::StorageId::Individuals, "test");
351 assert!(get_result.is_ok(), "Expected Ok, got: {:?}", get_result);
352 if let crate::common::StorageResult::Ok(value) = get_result {
353 assert_eq!(value, "value");
354 }
355 }
356
357 #[test]
358 fn test_storage_provider_generic_memory() {
359 let mut storage = StorageProvider::memory_generic();
360 assert!(!storage.is_empty());
361
362 assert!(storage.put_value(crate::common::StorageId::Individuals, "test", "value").is_ok());
364 let get_result = storage.get_value(crate::common::StorageId::Individuals, "test");
365 assert!(get_result.is_ok(), "Expected Ok, got: {:?}", get_result);
366 if let crate::common::StorageResult::Ok(value) = get_result {
367 assert_eq!(value, "value");
368 }
369 }
370
371 #[test]
372 fn test_storage_provider_lmdb() {
373 let _storage = StorageProvider::lmdb("/tmp/test", StorageMode::ReadOnly, None);
374 }
376
377 #[test]
378 fn test_storage_provider_remote() {
379 let _storage = StorageProvider::remote("127.0.0.1:8080");
380 }
382
383 #[test]
384 fn test_storage_provider_tarantool() {
385 let _storage = StorageProvider::tarantool("127.0.0.1:3301".to_string(), "user", "pass");
386 }
388}