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}