1use std::collections::HashMap;
7use crate::container::{IocContainer, IocContainerBuilder, ServiceConventions, ServiceLifetime};
8use crate::bootstrap::providers::{ProviderConfigurator, ConfigError};
9use crate::modules::CompileTimeModuleMetadata;
10
11pub struct AutoConfigBuilder {
13 builder: IocContainerBuilder,
14 conventions: ServiceConventions,
15 modules: Vec<CompileTimeModuleMetadata>,
16 custom_configurations: Vec<Box<dyn ConfigurationRule>>,
17}
18
19impl AutoConfigBuilder {
20 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 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 pub fn with_conventions(mut self, conventions: ServiceConventions) -> Self {
42 self.conventions = conventions;
43 self
44 }
45
46 pub fn with_modules(mut self, modules: Vec<CompileTimeModuleMetadata>) -> Self {
48 self.modules = modules;
49 self
50 }
51
52 pub fn add_module(mut self, module: CompileTimeModuleMetadata) -> Self {
54 self.modules.push(module);
55 self
56 }
57
58 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 pub fn build(self) -> Result<IocContainer, ConfigError> {
66 let container = self.builder.build()
68 .map_err(|e| ConfigError::ContainerError(e))?;
69
70 let mut configurator = ProviderConfigurator::with_conventions(container, self.conventions);
72
73 configurator.configure_from_modules(&self.modules)?;
75
76 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
91pub trait ConfigurationRule: Send + Sync {
93 fn apply(&self, configurator: &mut ProviderConfigurator) -> Result<(), ConfigError>;
95
96 fn description(&self) -> &'static str {
98 "Custom configuration rule"
99 }
100
101 fn priority(&self) -> u32 {
103 100
104 }
105}
106
107pub struct ContainerAutoConfig;
109
110impl ContainerAutoConfig {
111 pub fn from_modules(modules: Vec<CompileTimeModuleMetadata>) -> Result<IocContainer, ConfigError> {
113 AutoConfigBuilder::new()
114 .with_modules(modules)
115 .build()
116 }
117
118 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 pub fn validate_modules(modules: &[CompileTimeModuleMetadata]) -> Result<ValidationReport, ConfigError> {
131 let container = IocContainer::new();
132 let mut configurator = ProviderConfigurator::new(container);
133
134 configurator.configure_from_modules(modules)?;
136
137 Ok(ValidationReport::from_configurator(&configurator))
138 }
139}
140
141#[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 pub fn from_configurator(configurator: &ProviderConfigurator) -> Self {
153 let mut providers_by_lifetime = HashMap::new();
154
155 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 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 pub fn has_issues(&self) -> bool {
173 !self.potential_issues.is_empty()
174 }
175
176 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#[derive(Debug, Clone)]
198pub enum ValidationIssue {
199 TooManyDependencies {
201 service: String,
202 count: usize,
203 recommended_max: usize,
204 },
205 DeepDependencyChain {
207 service: String,
208 depth: usize,
209 recommended_max: usize,
210 },
211 PerformanceConcern {
213 service: String,
214 issue: String,
215 suggestion: String,
216 },
217 ConventionViolation {
219 service: String,
220 expected: String,
221 actual: String,
222 },
223}
224
225#[allow(dead_code)]
227pub mod rules {
228 use super::*;
229
230 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 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 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 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 pub struct LifetimeOptimizationRule;
286
287 impl ConfigurationRule for LifetimeOptimizationRule {
288 fn apply(&self, _configurator: &mut ProviderConfigurator) -> Result<(), ConfigError> {
289 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()); }
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 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}