Skip to main content

v_storage/
storage_factory.rs

1use crate::common::{Storage, StorageMode};
2use std::fmt;
3
4/// Абстрактная фабрика для создания различных типов хранилищ
5pub trait StorageFactory {
6    fn create_storage(&self) -> Result<Box<dyn Storage>, StorageError>;
7}
8
9/// Ошибки создания хранилищ
10#[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/// Конфигурация для различных типов хранилищ
30#[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
48/// Билдер для создания хранилищ через фабрику
49pub 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    // ========================================================================================
97    // НОВЫЕ МЕТОДЫ ДЛЯ СОЗДАНИЯ GENERIC ВЕРСИЙ
98    // ========================================================================================
99
100    /// Создает generic память хранилище
101    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    /// Создает generic LMDB хранилище
112    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    /// Создает generic удаленное хранилище
123    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    /// Создает generic Tarantool хранилище
134    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
151// ========================================================================================
152// STORAGE PROVIDER - ФАБРИЧНЫЕ МЕТОДЫ ДЛЯ УДОБНОГО СОЗДАНИЯ
153// ========================================================================================
154
155/// Провайдер хранилищ - централизованное место для создания всех типов хранилищ
156/// 
157/// Ответственности:
158/// - Создание конкретных экземпляров хранилищ
159/// - Логирование процесса создания
160/// - Конфигурация параметров по умолчанию
161pub struct StorageProvider;
162
163impl StorageProvider {
164    /// Создает новое хранилище в памяти (dynamic dispatch)
165    pub fn memory() -> Box<dyn Storage> {
166        log::info!("Creating in-memory storage");
167        Box::new(crate::memory_storage::MemoryStorage::new())
168    }
169
170    /// Создает новое LMDB хранилище (dynamic dispatch)
171    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    /// Создает новое удаленное хранилище (dynamic dispatch)
177    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    /// Создает новое Tarantool хранилище (dynamic dispatch)
183    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    /// Создает VStorage с памятью
189    pub fn vstorage_memory() -> crate::vstorage::VStorage {
190        crate::vstorage::VStorage::new(Self::memory())
191    }
192
193    /// Создает VStorage с LMDB
194    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    /// Создает VStorage с удаленным хранилищем
199    pub fn vstorage_remote(addr: &str) -> crate::vstorage::VStorage {
200        crate::vstorage::VStorage::new(Self::remote(addr))
201    }
202
203    /// Создает VStorage с Tarantool
204    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    // ========================================================================================
209    // ФАБРИЧНЫЕ МЕТОДЫ ДЛЯ GENERIC ВЕРСИЙ (static dispatch)
210    // ========================================================================================
211
212    /// Создает generic хранилище в памяти
213    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    /// Создает generic LMDB хранилище
219    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    /// Создает generic удаленное хранилище
225    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    /// Создает generic Tarantool хранилище
231    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
237/// Реализация фабрики по умолчанию
238pub 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    // ========================================================================================
334    // ТЕСТЫ ДЛЯ STORAGE PROVIDER
335    // ========================================================================================
336
337    #[test]
338    fn test_storage_provider_memory() {
339        let _storage = StorageProvider::memory();
340        // Проверяем что создание прошло без panic
341    }
342
343    #[test]
344    fn test_storage_provider_vstorage_memory() {
345        let mut storage = StorageProvider::vstorage_memory();
346        assert!(!storage.is_empty());
347        
348        // Проверяем базовые операции с новым API
349        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        // Проверяем базовые операции с новым API
363        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        // Проверяем что создание прошло без panic
375    }
376
377    #[test]
378    fn test_storage_provider_remote() {
379        let _storage = StorageProvider::remote("127.0.0.1:8080");
380        // Проверяем что создание прошло без panic
381    }
382
383    #[test]
384    fn test_storage_provider_tarantool() {
385        let _storage = StorageProvider::tarantool("127.0.0.1:3301".to_string(), "user", "pass");
386        // Проверяем что создание прошло без panic
387    }
388}