elif_core/container/
auto_config.rs

1//! Automatic Container Configuration
2//!
3//! This module provides automatic container configuration based on conventions
4//! and metadata, integrating with the provider auto-configuration system.
5
6use std::collections::HashMap;
7use crate::container::{IocContainer, IocContainerBuilder, ServiceConventions, ServiceLifetime};
8use crate::bootstrap::providers::{ProviderConfigurator, ConfigError};
9use crate::modules::CompileTimeModuleMetadata;
10
11/// Automatic configuration builder for IoC container
12pub struct AutoConfigBuilder {
13    builder: IocContainerBuilder,
14    conventions: ServiceConventions,
15    modules: Vec<CompileTimeModuleMetadata>,
16    custom_configurations: Vec<Box<dyn ConfigurationRule>>,
17}
18
19impl AutoConfigBuilder {
20    /// Create a new auto-configuration builder
21    pub fn new() -> Self {
22        Self {
23            builder: IocContainerBuilder::new(),
24            conventions: ServiceConventions::new(),
25            modules: Vec::new(),
26            custom_configurations: Vec::new(),
27        }
28    }
29    
30    /// Create auto-configuration builder with existing IoC builder
31    pub fn with_builder(builder: IocContainerBuilder) -> Self {
32        Self {
33            builder,
34            conventions: ServiceConventions::new(),
35            modules: Vec::new(),
36            custom_configurations: Vec::new(),
37        }
38    }
39    
40    /// Set custom service conventions
41    pub fn with_conventions(mut self, conventions: ServiceConventions) -> Self {
42        self.conventions = conventions;
43        self
44    }
45    
46    /// Add modules to configure
47    pub fn with_modules(mut self, modules: Vec<CompileTimeModuleMetadata>) -> Self {
48        self.modules = modules;
49        self
50    }
51    
52    /// Add a single module to configure
53    pub fn add_module(mut self, module: CompileTimeModuleMetadata) -> Self {
54        self.modules.push(module);
55        self
56    }
57    
58    /// Add a custom configuration rule
59    pub fn add_configuration_rule<R: ConfigurationRule + 'static>(mut self, rule: R) -> Self {
60        self.custom_configurations.push(Box::new(rule));
61        self
62    }
63    
64    /// Build the container with automatic configuration
65    pub fn build(self) -> Result<IocContainer, ConfigError> {
66        // Build basic container
67        let container = self.builder.build()
68            .map_err(|e| ConfigError::ContainerError(e))?;
69        
70        // Create provider configurator
71        let mut configurator = ProviderConfigurator::with_conventions(container, self.conventions);
72        
73        // Configure from modules
74        configurator.configure_from_modules(&self.modules)?;
75        
76        // Apply custom configuration rules
77        for rule in &self.custom_configurations {
78            rule.apply(&mut configurator)?;
79        }
80        
81        Ok(configurator.into_container())
82    }
83}
84
85impl Default for AutoConfigBuilder {
86    fn default() -> Self {
87        Self::new()
88    }
89}
90
91/// Trait for custom configuration rules
92pub trait ConfigurationRule: Send + Sync {
93    /// Apply the configuration rule to the provider configurator
94    fn apply(&self, configurator: &mut ProviderConfigurator) -> Result<(), ConfigError>;
95    
96    /// Get a description of what this rule does
97    fn description(&self) -> &'static str {
98        "Custom configuration rule"
99    }
100    
101    /// Get the priority of this rule (higher numbers run first)
102    fn priority(&self) -> u32 {
103        100
104    }
105}
106
107/// Container configuration utilities
108pub struct ContainerAutoConfig;
109
110impl ContainerAutoConfig {
111    /// Create a fully auto-configured container from modules
112    pub fn from_modules(modules: Vec<CompileTimeModuleMetadata>) -> Result<IocContainer, ConfigError> {
113        AutoConfigBuilder::new()
114            .with_modules(modules)
115            .build()
116    }
117    
118    /// Create a fully auto-configured container from modules with custom conventions
119    pub fn from_modules_with_conventions(
120        modules: Vec<CompileTimeModuleMetadata>,
121        conventions: ServiceConventions,
122    ) -> Result<IocContainer, ConfigError> {
123        AutoConfigBuilder::new()
124            .with_modules(modules)
125            .with_conventions(conventions)
126            .build()
127    }
128    
129    /// Validate module configuration without building container
130    pub fn validate_modules(modules: &[CompileTimeModuleMetadata]) -> Result<ValidationReport, ConfigError> {
131        let container = IocContainer::new();
132        let mut configurator = ProviderConfigurator::new(container);
133        
134        // Extract providers and validate
135        configurator.configure_from_modules(modules)?;
136        
137        Ok(ValidationReport::from_configurator(&configurator))
138    }
139}
140
141/// Validation report for container configuration
142#[derive(Debug)]
143pub struct ValidationReport {
144    pub total_providers: usize,
145    pub providers_by_lifetime: HashMap<ServiceLifetime, usize>,
146    pub dependency_graph_depth: usize,
147    pub potential_issues: Vec<ValidationIssue>,
148}
149
150impl ValidationReport {
151    /// Create validation report from provider configurator
152    pub fn from_configurator(configurator: &ProviderConfigurator) -> Self {
153        let mut providers_by_lifetime = HashMap::new();
154
155        // This assumes a `providers()` method is added to `ProviderConfigurator`
156        for provider in configurator.providers() {
157            let lifetime = provider.lifetime.unwrap_or(ServiceLifetime::Transient);
158            *providers_by_lifetime.entry(lifetime).or_default() += 1;
159        }
160
161        // For now, create a partial report
162        // TODO: Calculate dependency_graph_depth and potential_issues
163        Self {
164            total_providers: configurator.providers().len(),
165            providers_by_lifetime,
166            dependency_graph_depth: 0,
167            potential_issues: Vec::new(),
168        }
169    }
170    
171    /// Check if the configuration has any issues
172    pub fn has_issues(&self) -> bool {
173        !self.potential_issues.is_empty()
174    }
175    
176    /// Get summary statistics
177    pub fn summary(&self) -> String {
178        format!(
179            "Configuration Summary:\n\
180            - Total Providers: {}\n\
181            - Singleton Services: {}\n\
182            - Scoped Services: {}\n\
183            - Transient Services: {}\n\
184            - Dependency Depth: {}\n\
185            - Issues Found: {}",
186            self.total_providers,
187            self.providers_by_lifetime.get(&ServiceLifetime::Singleton).unwrap_or(&0),
188            self.providers_by_lifetime.get(&ServiceLifetime::Scoped).unwrap_or(&0),
189            self.providers_by_lifetime.get(&ServiceLifetime::Transient).unwrap_or(&0),
190            self.dependency_graph_depth,
191            self.potential_issues.len()
192        )
193    }
194}
195
196/// Validation issue types
197#[derive(Debug, Clone)]
198pub enum ValidationIssue {
199    /// Service has too many dependencies
200    TooManyDependencies {
201        service: String,
202        count: usize,
203        recommended_max: usize,
204    },
205    /// Deep dependency chain
206    DeepDependencyChain {
207        service: String,
208        depth: usize,
209        recommended_max: usize,
210    },
211    /// Potential performance concern
212    PerformanceConcern {
213        service: String,
214        issue: String,
215        suggestion: String,
216    },
217    /// Convention violation
218    ConventionViolation {
219        service: String,
220        expected: String,
221        actual: String,
222    },
223}
224
225/// Pre-built configuration rules
226#[allow(dead_code)]
227pub mod rules {
228    use super::*;
229    
230    /// Rule that validates service dependency counts
231    pub struct DependencyCountRule {
232        max_dependencies: usize,
233    }
234    
235    impl DependencyCountRule {
236        pub fn new(max_dependencies: usize) -> Self {
237            Self { max_dependencies }
238        }
239    }
240    
241    impl ConfigurationRule for DependencyCountRule {
242        fn apply(&self, _configurator: &mut ProviderConfigurator) -> Result<(), ConfigError> {
243            // This would validate dependency counts and add warnings
244            // For now, just a placeholder
245            Ok(())
246        }
247        
248        fn description(&self) -> &'static str {
249            "Validates that services don't have too many dependencies"
250        }
251        
252        fn priority(&self) -> u32 {
253            200
254        }
255    }
256    
257    /// Rule that enforces naming conventions more strictly
258    pub struct NamingConventionRule {
259        strict_mode: bool,
260    }
261    
262    impl NamingConventionRule {
263        pub fn new(strict_mode: bool) -> Self {
264            Self { strict_mode }
265        }
266    }
267    
268    impl ConfigurationRule for NamingConventionRule {
269        fn apply(&self, _configurator: &mut ProviderConfigurator) -> Result<(), ConfigError> {
270            // This would enforce naming conventions more strictly
271            // For now, just a placeholder
272            Ok(())
273        }
274        
275        fn description(&self) -> &'static str {
276            "Enforces strict naming conventions for services"
277        }
278        
279        fn priority(&self) -> u32 {
280            150
281        }
282    }
283    
284    /// Rule that optimizes service lifetimes based on usage patterns
285    pub struct LifetimeOptimizationRule;
286    
287    impl ConfigurationRule for LifetimeOptimizationRule {
288        fn apply(&self, _configurator: &mut ProviderConfigurator) -> Result<(), ConfigError> {
289            // This would analyze usage patterns and suggest lifetime optimizations
290            // For now, just a placeholder
291            Ok(())
292        }
293        
294        fn description(&self) -> &'static str {
295            "Optimizes service lifetimes based on dependency patterns"
296        }
297        
298        fn priority(&self) -> u32 {
299            50
300        }
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307    use crate::modules::CompileTimeModuleMetadata;
308    
309    #[test]
310    fn test_auto_config_builder() {
311        let module = CompileTimeModuleMetadata::new("TestModule".to_string())
312            .with_providers(vec!["UserService".to_string(), "UserRepository".to_string()]);
313        
314        let result = AutoConfigBuilder::new()
315            .add_module(module)
316            .build();
317        
318        assert!(result.is_ok());
319    }
320    
321    #[test]
322    fn test_container_auto_config_from_modules() {
323        let modules = vec![
324            CompileTimeModuleMetadata::new("UserModule".to_string())
325                .with_providers(vec!["UserService".to_string()]),
326            CompileTimeModuleMetadata::new("AuthModule".to_string())
327                .with_providers(vec!["AuthService".to_string()]),
328        ];
329        
330        let result = ContainerAutoConfig::from_modules(modules);
331        assert!(result.is_ok());
332    }
333    
334    #[test]
335    fn test_validation_report() {
336        let modules = vec![
337            CompileTimeModuleMetadata::new("TestModule".to_string())
338                .with_providers(vec!["TestService".to_string()]),
339        ];
340        
341        let result = ContainerAutoConfig::validate_modules(&modules);
342        assert!(result.is_ok());
343        
344        let report = result.unwrap();
345        assert!(!report.has_issues()); // Should be valid
346    }
347    
348    #[test]
349    fn test_custom_configuration_rule() {
350        struct TestRule;
351        
352        impl ConfigurationRule for TestRule {
353            fn apply(&self, _configurator: &mut ProviderConfigurator) -> Result<(), ConfigError> {
354                Ok(())
355            }
356        }
357        
358        let module = CompileTimeModuleMetadata::new("TestModule".to_string())
359            .with_providers(vec!["TestService".to_string()]);
360        
361        let result = AutoConfigBuilder::new()
362            .add_module(module)
363            .add_configuration_rule(TestRule)
364            .build();
365        
366        assert!(result.is_ok());
367    }
368    
369    #[test]
370    fn test_validation_report_summary() {
371        let report = ValidationReport {
372            total_providers: 5,
373            providers_by_lifetime: [
374                (ServiceLifetime::Singleton, 2),
375                (ServiceLifetime::Scoped, 2),
376                (ServiceLifetime::Transient, 1),
377            ].iter().cloned().collect(),
378            dependency_graph_depth: 3,
379            potential_issues: vec![],
380        };
381        
382        let summary = report.summary();
383        assert!(summary.contains("Total Providers: 5"));
384        assert!(summary.contains("Singleton Services: 2"));
385        assert!(summary.contains("Scoped Services: 2"));
386        assert!(summary.contains("Transient Services: 1"));
387    }
388    
389    #[test]
390    fn test_configuration_rules() {
391        use rules::*;
392        
393        // Test that rules can be created and have correct properties
394        let dep_rule = DependencyCountRule::new(10);
395        assert_eq!(dep_rule.priority(), 200);
396        assert!(dep_rule.description().contains("dependencies"));
397        
398        let naming_rule = NamingConventionRule::new(true);
399        assert_eq!(naming_rule.priority(), 150);
400        assert!(naming_rule.description().contains("naming"));
401        
402        let lifetime_rule = LifetimeOptimizationRule;
403        assert_eq!(lifetime_rule.priority(), 50);
404        assert!(lifetime_rule.description().contains("lifetime"));
405    }
406}