subx_cli/core/
services.rs

1//! Service container for dependency management and injection.
2//!
3//! This module provides a centralized service container that manages
4//! the lifecycle of services and components, enabling clean dependency
5//! injection throughout the application.
6
7use crate::{Result, config::ConfigService, core::ComponentFactory};
8use std::sync::Arc;
9
10/// Service container for dependency injection and service management.
11///
12/// The service container holds references to core services and provides
13/// a centralized way to access them throughout the application. It manages
14/// the lifecycle of services and ensures proper dependency injection.
15///
16/// # Design Principles
17///
18/// - **Single Source of Truth**: All services are managed through the container
19/// - **Dependency Injection**: Components receive dependencies explicitly
20/// - **Configuration Isolation**: Services are decoupled from global configuration
21/// - **Test Friendliness**: Easy to mock and test individual components
22///
23/// # Examples
24///
25/// ```rust
26/// use subx_cli::core::ServiceContainer;
27/// use subx_cli::config::ProductionConfigService;
28/// use std::sync::Arc;
29///
30/// # async fn example() -> subx_cli::Result<()> {
31/// let config_service = Arc::new(ProductionConfigService::new()?);
32/// let container = ServiceContainer::new(config_service)?;
33///
34/// // Access services through container
35/// let config_service = container.config_service();
36/// let factory = container.component_factory();
37/// # Ok(())
38/// # }
39/// ```
40pub struct ServiceContainer {
41    config_service: Arc<dyn ConfigService>,
42    component_factory: ComponentFactory,
43}
44
45impl ServiceContainer {
46    /// Create a new service container with the given configuration service.
47    ///
48    /// # Arguments
49    ///
50    /// * `config_service` - Configuration service implementation
51    ///
52    /// # Errors
53    ///
54    /// Returns an error if component factory creation fails.
55    pub fn new(config_service: Arc<dyn ConfigService>) -> Result<Self> {
56        let component_factory = ComponentFactory::new(config_service.as_ref())?;
57
58        Ok(Self {
59            config_service,
60            component_factory,
61        })
62    }
63
64    /// Get a reference to the configuration service.
65    ///
66    /// Returns a reference to the configuration service managed by this container.
67    pub fn config_service(&self) -> &Arc<dyn ConfigService> {
68        &self.config_service
69    }
70
71    /// Get a reference to the component factory.
72    ///
73    /// Returns a reference to the component factory that can create
74    /// configured instances of core components.
75    pub fn component_factory(&self) -> &ComponentFactory {
76        &self.component_factory
77    }
78
79    /// Reload all services and components.
80    ///
81    /// This method triggers a reload of the configuration service and
82    /// recreates the component factory with the updated configuration.
83    /// This is useful for dynamic configuration updates.
84    ///
85    /// # Errors
86    ///
87    /// Returns an error if configuration reloading or factory recreation fails.
88    pub fn reload(&mut self) -> Result<()> {
89        // Reload configuration service
90        self.config_service.reload()?;
91
92        // Recreate component factory with updated configuration
93        self.component_factory = ComponentFactory::new(self.config_service.as_ref())?;
94
95        Ok(())
96    }
97
98    /// Create a new service container for testing with custom configuration.
99    ///
100    /// This method is useful for testing scenarios where you need to provide
101    /// specific configuration values.
102    ///
103    /// # Arguments
104    ///
105    /// * `config_service` - Test configuration service
106    ///
107    /// # Errors
108    ///
109    /// Returns an error if container creation fails.
110    #[cfg(test)]
111    pub fn new_for_testing(config_service: Arc<dyn ConfigService>) -> Result<Self> {
112        Self::new(config_service)
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use crate::config::test_service::TestConfigService;
120
121    #[test]
122    fn test_service_container_creation() {
123        let config_service = Arc::new(TestConfigService::default());
124        let container = ServiceContainer::new(config_service);
125        assert!(container.is_ok());
126    }
127
128    #[test]
129    fn test_service_container_access() {
130        let config_service = Arc::new(TestConfigService::default());
131        let container = ServiceContainer::new(config_service.clone()).unwrap();
132
133        // Test that we can access services
134        let _retrieved_config_service = container.config_service();
135        // Note: Can't test pointer equality due to trait object casting
136
137        let factory = container.component_factory();
138        assert!(factory.config().ai.provider == "openai");
139    }
140
141    #[test]
142    fn test_service_container_reload() {
143        let config_service = Arc::new(TestConfigService::default());
144        let mut container = ServiceContainer::new(config_service).unwrap();
145
146        // Test reload operation
147        let result = container.reload();
148        assert!(result.is_ok());
149    }
150
151    #[test]
152    fn test_new_for_testing() {
153        let config_service = Arc::new(TestConfigService::default());
154        let container = ServiceContainer::new_for_testing(config_service);
155        assert!(container.is_ok());
156    }
157}