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}