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