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    #[cfg(any(feature = "tt_2", feature = "tt_3"))]
42    Tarantool {
43        uri: String,
44        login: String,
45        password: String,
46    },
47}
48
49/// Билдер для создания хранилищ через фабрику
50pub 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    // ========================================================================================
99    // НОВЫЕ МЕТОДЫ ДЛЯ СОЗДАНИЯ GENERIC ВЕРСИЙ
100    // ========================================================================================
101
102    /// Создает generic память хранилище
103    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    /// Создает generic LMDB хранилище
114    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    /// Создает generic удаленное хранилище
125    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    /// Создает generic Tarantool хранилище
136    #[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
154// ========================================================================================
155// STORAGE PROVIDER - ФАБРИЧНЫЕ МЕТОДЫ ДЛЯ УДОБНОГО СОЗДАНИЯ
156// ========================================================================================
157
158/// Провайдер хранилищ - централизованное место для создания всех типов хранилищ
159/// 
160/// Ответственности:
161/// - Создание конкретных экземпляров хранилищ
162/// - Логирование процесса создания
163/// - Конфигурация параметров по умолчанию
164pub struct StorageProvider;
165
166impl StorageProvider {
167    /// Создает новое хранилище в памяти (dynamic dispatch)
168    pub fn memory() -> Box<dyn Storage> {
169        log::info!("Creating in-memory storage");
170        Box::new(crate::memory_storage::MemoryStorage::new())
171    }
172
173    /// Создает новое LMDB хранилище (dynamic dispatch)
174    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    /// Создает новое удаленное хранилище (dynamic dispatch)
180    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    /// Создает новое Tarantool хранилище (dynamic dispatch)
186    #[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    /// Создает VStorage с памятью
193    pub fn vstorage_memory() -> crate::vstorage::VStorage {
194        crate::vstorage::VStorage::new(Self::memory())
195    }
196
197    /// Создает VStorage с LMDB
198    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    /// Создает VStorage с удаленным хранилищем
203    pub fn vstorage_remote(addr: &str) -> crate::vstorage::VStorage {
204        crate::vstorage::VStorage::new(Self::remote(addr))
205    }
206
207    /// Создает VStorage с Tarantool
208    #[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    // ========================================================================================
214    // ФАБРИЧНЫЕ МЕТОДЫ ДЛЯ GENERIC ВЕРСИЙ (static dispatch)
215    // ========================================================================================
216
217    /// Создает generic хранилище в памяти
218    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    /// Создает generic LMDB хранилище
224    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    /// Создает generic удаленное хранилище
230    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    /// Создает generic Tarantool хранилище
236    #[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
243/// Реализация фабрики по умолчанию
244pub 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    // ========================================================================================
342    // ТЕСТЫ ДЛЯ STORAGE PROVIDER
343    // ========================================================================================
344
345    #[test]
346    fn test_storage_provider_memory() {
347        let _storage = StorageProvider::memory();
348        // Проверяем что создание прошло без panic
349    }
350
351    #[test]
352    fn test_storage_provider_vstorage_memory() {
353        let mut storage = StorageProvider::vstorage_memory();
354        assert!(!storage.is_empty());
355        
356        // Проверяем базовые операции с новым API
357        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        // Проверяем базовые операции с новым API
371        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        // Проверяем что создание прошло без panic
383    }
384
385    #[test]
386    fn test_storage_provider_remote() {
387        let _storage = StorageProvider::remote("127.0.0.1:8080");
388        // Проверяем что создание прошло без panic
389    }
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        // Проверяем что создание прошло без panic
396    }
397}