subx_cli/core/
factory.rs

1//! Component factory for creating configured instances of core components.
2//!
3//! This module provides a centralized factory for creating instances of core
4//! components with proper configuration injection, eliminating the need for
5//! global configuration access within individual components.
6
7use crate::{
8    Result,
9    config::{Config, ConfigService},
10    core::{file_manager::FileManager, matcher::engine::MatchEngine},
11    error::SubXError,
12    services::ai::AIProvider,
13};
14
15/// Component factory for creating configured instances.
16///
17/// This factory provides a centralized way to create core components
18/// with proper configuration injection, ensuring consistent component
19/// initialization across the application.
20///
21/// # Examples
22///
23/// ```rust
24/// use subx_cli::core::ComponentFactory;
25/// use subx_cli::config::ProductionConfigService;
26/// use std::sync::Arc;
27///
28/// # async fn example() -> subx_cli::Result<()> {
29/// let config_service = Arc::new(ProductionConfigService::new()?);
30/// let factory = ComponentFactory::new(config_service.as_ref())?;
31///
32/// // Create components with proper configuration
33/// let match_engine = factory.create_match_engine()?;
34/// let file_manager = factory.create_file_manager();
35/// let ai_provider = factory.create_ai_provider()?;
36/// # Ok(())
37/// # }
38/// ```
39pub struct ComponentFactory {
40    config: Config,
41}
42
43impl ComponentFactory {
44    /// Create a new component factory with the given configuration service.
45    ///
46    /// # Arguments
47    ///
48    /// * `config_service` - Configuration service to load configuration from
49    ///
50    /// # Errors
51    ///
52    /// Returns an error if configuration loading fails.
53    pub fn new(config_service: &dyn ConfigService) -> Result<Self> {
54        let config = config_service.get_config()?;
55        Ok(Self { config })
56    }
57
58    /// Create a match engine with AI configuration.
59    ///
60    /// Returns a properly configured MatchEngine instance using
61    /// the AI configuration section.
62    ///
63    /// # Errors
64    ///
65    /// Returns an error if AI provider creation fails.
66    pub fn create_match_engine(&self) -> Result<MatchEngine> {
67        let ai_provider = self.create_ai_provider()?;
68        let match_config = crate::core::matcher::MatchConfig {
69            confidence_threshold: 0.8, // Default value, can be configurable
70            max_sample_length: self.config.ai.max_sample_length,
71            enable_content_analysis: true,
72            backup_enabled: self.config.general.backup_enabled,
73            relocation_mode: crate::core::matcher::engine::FileRelocationMode::None,
74            conflict_resolution: crate::core::matcher::engine::ConflictResolution::AutoRename,
75            ai_model: self.config.ai.model.clone(),
76        };
77        Ok(MatchEngine::new(ai_provider, match_config))
78    }
79
80    /// Create a file manager with general configuration.
81    ///
82    /// Returns a properly configured FileManager instance using
83    /// the general configuration section.
84    pub fn create_file_manager(&self) -> FileManager {
85        // For now, FileManager doesn't take configuration in its constructor
86        // This will be updated when FileManager is refactored to accept config
87        FileManager::new()
88    }
89
90    /// Create an AI provider with AI configuration.
91    ///
92    /// Returns a properly configured AI provider instance based on
93    /// the provider type specified in the AI configuration.
94    ///
95    /// # Errors
96    ///
97    /// Returns an error if the provider type is unsupported or
98    /// provider creation fails.
99    pub fn create_ai_provider(&self) -> Result<Box<dyn AIProvider>> {
100        create_ai_provider(&self.config.ai)
101    }
102
103    /// Get a reference to the current configuration.
104    ///
105    /// Returns a reference to the configuration used by this factory.
106    pub fn config(&self) -> &Config {
107        &self.config
108    }
109}
110
111/// Create an AI provider from AI configuration.
112///
113/// This function creates the appropriate AI provider based on the
114/// provider type specified in the configuration.
115///
116/// # Arguments
117///
118/// * `ai_config` - AI configuration containing provider settings
119///
120/// # Errors
121///
122/// Returns an error if the provider type is unsupported or creation fails.
123pub fn create_ai_provider(ai_config: &crate::config::AIConfig) -> Result<Box<dyn AIProvider>> {
124    match ai_config.provider.as_str() {
125        "openai" => {
126            // For now, just create a mock provider since the actual implementation might not be ready
127            Err(SubXError::config(
128                "AI provider creation not yet implemented",
129            ))
130        }
131        _ => Err(SubXError::config(format!(
132            "Unsupported AI provider: {}",
133            ai_config.provider
134        ))),
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use crate::config::test_service::TestConfigService;
142
143    #[test]
144    fn test_component_factory_creation() {
145        let config_service = TestConfigService::default();
146        let factory = ComponentFactory::new(&config_service);
147        assert!(factory.is_ok());
148    }
149
150    #[test]
151    fn test_factory_creation() {
152        let config_service = TestConfigService::default();
153        let factory = ComponentFactory::new(&config_service);
154        assert!(factory.is_ok());
155    }
156
157    #[test]
158    fn test_create_file_manager() {
159        let config_service = TestConfigService::default();
160        let factory = ComponentFactory::new(&config_service).unwrap();
161
162        let _file_manager = factory.create_file_manager();
163        // Basic validation that file manager was created
164        // FileManager doesn't expose config yet, so just verify creation succeeds
165    }
166
167    #[test]
168    fn test_unsupported_ai_provider() {
169        let mut config = crate::config::Config::default();
170        config.ai.provider = "unsupported".to_string();
171
172        let result: Result<Box<dyn AIProvider>> = create_ai_provider(&config.ai);
173        assert!(result.is_err());
174
175        match result {
176            Err(e) => {
177                let error_msg = e.to_string();
178                assert!(error_msg.contains("Unsupported AI provider"));
179            }
180            Ok(_) => panic!("Expected error for unsupported provider"),
181        }
182    }
183}