// factory.rs // // Factory function to create storage backends based on configuration. use std::sync::Arc; use tracing::info; use url::Url; use super::{ core::{ConversationItemStorage, ConversationStorage, ResponseStorage}, memory::{MemoryConversationItemStorage, MemoryConversationStorage, MemoryResponseStorage}, noop::{NoOpConversationItemStorage, NoOpConversationStorage, NoOpResponseStorage}, oracle::{OracleConversationItemStorage, OracleConversationStorage, OracleResponseStorage}, }; use crate::{ config::{HistoryBackend, OracleConfig, PostgresConfig, RedisConfig, RouterConfig}, data_connector::{ postgres::{ PostgresConversationItemStorage, PostgresConversationStorage, PostgresResponseStorage, PostgresStore, }, redis::{ RedisConversationItemStorage, RedisConversationStorage, RedisResponseStorage, RedisStore, }, }, }; /// Type alias for the storage tuple returned by factory functions. /// This avoids clippy::type_complexity warnings while keeping Arc explicit. pub type StorageTuple = ( Arc, Arc, Arc, ); /// Create all three storage backends based on router configuration. /// /// # Arguments /// * `config` - Router configuration containing history_backend and oracle settings /// /// # Returns /// Tuple of (response_storage, conversation_storage, conversation_item_storage) /// /// # Errors /// Returns error string if Oracle configuration is missing or initialization fails pub fn create_storage(config: &RouterConfig) -> Result { match config.history_backend { HistoryBackend::Memory => { info!("Initializing data connector: Memory"); Ok(( Arc::new(MemoryResponseStorage::new()), Arc::new(MemoryConversationStorage::new()), Arc::new(MemoryConversationItemStorage::new()), )) } HistoryBackend::None => { info!("Initializing data connector: None (no persistence)"); Ok(( Arc::new(NoOpResponseStorage::new()), Arc::new(NoOpConversationStorage::new()), Arc::new(NoOpConversationItemStorage::new()), )) } HistoryBackend::Oracle => { let oracle_cfg = config .oracle .clone() .ok_or("oracle configuration is required when history_backend=oracle")?; info!( "Initializing data connector: Oracle ATP (pool: {}-{})", oracle_cfg.pool_min, oracle_cfg.pool_max ); let storages = create_oracle_storage(&oracle_cfg)?; info!("Data connector initialized successfully: Oracle ATP"); Ok(storages) } HistoryBackend::Postgres => { let postgres_cfg = config .postgres .clone() .ok_or("Postgres configuration is required when history_backend=postgres")?; let log_db_url = match Url::parse(&postgres_cfg.db_url) { Ok(mut url) => { if url.password().is_some() { let _ = url.set_password(Some("****")); } url.to_string() } Err(_) => "".to_string(), }; info!( "Initializing data connector: Postgres (db_url: {}, pool_max: {})", log_db_url, postgres_cfg.pool_max ); let storages = create_postgres_storage(&postgres_cfg)?; info!("Data connector initialized successfully: Postgres"); Ok(storages) } HistoryBackend::Redis => { let redis_cfg = config .redis .clone() .ok_or("Redis configuration is required when history_backend=redis")?; let log_redis_url = match Url::parse(&redis_cfg.url) { Ok(mut url) => { if url.password().is_some() { let _ = url.set_password(Some("****")); } url.to_string() } Err(_) => "".to_string(), }; info!( "Initializing data connector: Redis (url: {}, pool_max: {})", log_redis_url, redis_cfg.pool_max ); let storages = create_redis_storage(&redis_cfg)?; info!("Data connector initialized successfully: Redis"); Ok(storages) } } } /// Create Oracle storage backends fn create_oracle_storage(oracle_cfg: &OracleConfig) -> Result { let response_storage = OracleResponseStorage::new(oracle_cfg.clone()) .map_err(|err| format!("failed to initialize Oracle response storage: {err}"))?; let conversation_storage = OracleConversationStorage::new(oracle_cfg.clone()) .map_err(|err| format!("failed to initialize Oracle conversation storage: {err}"))?; let conversation_item_storage = OracleConversationItemStorage::new(oracle_cfg.clone()) .map_err(|err| format!("failed to initialize Oracle conversation item storage: {err}"))?; Ok(( Arc::new(response_storage), Arc::new(conversation_storage), Arc::new(conversation_item_storage), )) } fn create_postgres_storage(postgres_cfg: &PostgresConfig) -> Result { let store = PostgresStore::new(postgres_cfg.clone())?; let postgres_resp = PostgresResponseStorage::new(store.clone()) .map_err(|err| format!("failed to initialize Postgres response storage: {err}"))?; let postgres_conv = PostgresConversationStorage::new(store.clone()) .map_err(|err| format!("failed to initialize Postgres conversation storage: {err}"))?; let postgres_item = PostgresConversationItemStorage::new(store.clone()) .map_err(|err| format!("failed to initialize Postgres conversation item storage: {err}"))?; Ok(( Arc::new(postgres_resp), Arc::new(postgres_conv), Arc::new(postgres_item), )) } fn create_redis_storage(redis_cfg: &RedisConfig) -> Result { let store = RedisStore::new(redis_cfg.clone())?; let redis_resp = RedisResponseStorage::new(store.clone()); let redis_conv = RedisConversationStorage::new(store.clone()); let redis_item = RedisConversationItemStorage::new(store.clone()); Ok(( Arc::new(redis_resp), Arc::new(redis_conv), Arc::new(redis_item), )) }