1use crate::container::{ContainerBuilder, IocContainer};
13use crate::modules::ModuleError;
14use std::any::TypeId;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum ServiceLifecycle {
19 Singleton,
21 Scoped,
23 Transient,
25}
26
27impl Default for ServiceLifecycle {
28 fn default() -> Self {
29 Self::Singleton
30 }
31}
32
33#[derive(Debug, Clone)]
35pub struct ServiceDescriptor {
36 pub service_name: String,
38 pub service_type: TypeId,
40 pub implementation_type: Option<TypeId>,
42 pub name: Option<String>,
44 pub lifecycle: ServiceLifecycle,
46 pub is_trait_service: bool,
48 pub dependencies: Vec<TypeId>,
50}
51
52impl ServiceDescriptor {
53 pub fn new<S: 'static>(service_name: impl Into<String>, lifecycle: ServiceLifecycle) -> Self {
55 Self {
56 service_name: service_name.into(),
57 service_type: TypeId::of::<S>(),
58 implementation_type: None,
59 name: None,
60 lifecycle,
61 is_trait_service: false,
62 dependencies: Vec::new(),
63 }
64 }
65
66 pub fn trait_mapping<S: 'static, I: 'static>(
68 service_name: impl Into<String>,
69 implementation_name: impl Into<String>,
70 lifecycle: ServiceLifecycle,
71 ) -> Self {
72 Self {
73 service_name: format!("{} => {}", service_name.into(), implementation_name.into()),
74 service_type: TypeId::of::<S>(),
75 implementation_type: Some(TypeId::of::<I>()),
76 name: None,
77 lifecycle,
78 is_trait_service: true,
79 dependencies: Vec::new(),
80 }
81 }
82
83 pub fn with_name(mut self, name: impl Into<String>) -> Self {
85 self.name = Some(name.into());
86 self
87 }
88
89 pub fn with_dependencies(mut self, dependencies: Vec<TypeId>) -> Self {
91 self.dependencies = dependencies;
92 self
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct ControllerDescriptor {
99 pub controller_name: String,
101 pub controller_type: TypeId,
103 pub base_path: Option<String>,
105 pub middleware: Vec<String>,
107 pub dependencies: Vec<TypeId>,
109}
110
111impl ControllerDescriptor {
112 pub fn new<C: 'static>(controller_name: impl Into<String>) -> Self {
114 Self {
115 controller_name: controller_name.into(),
116 controller_type: TypeId::of::<C>(),
117 base_path: None,
118 middleware: Vec::new(),
119 dependencies: Vec::new(),
120 }
121 }
122
123 pub fn with_base_path(mut self, path: impl Into<String>) -> Self {
125 self.base_path = Some(path.into());
126 self
127 }
128
129 pub fn with_middleware(mut self, middleware: Vec<String>) -> Self {
131 self.middleware = middleware;
132 self
133 }
134
135 pub fn with_dependencies(mut self, dependencies: Vec<TypeId>) -> Self {
137 self.dependencies = dependencies;
138 self
139 }
140}
141
142#[derive(Debug, Clone)]
144pub struct ModuleDescriptor {
145 pub name: String,
147 pub version: Option<String>,
149 pub description: Option<String>,
151 pub providers: Vec<ServiceDescriptor>,
153 pub controllers: Vec<ControllerDescriptor>,
155 pub imports: Vec<String>,
157 pub exports: Vec<String>,
159 pub dependencies: Vec<String>,
161 pub is_optional: bool,
163}
164
165impl ModuleDescriptor {
166 pub fn new(name: impl Into<String>) -> Self {
168 Self {
169 name: name.into(),
170 version: None,
171 description: None,
172 providers: Vec::new(),
173 controllers: Vec::new(),
174 imports: Vec::new(),
175 exports: Vec::new(),
176 dependencies: Vec::new(),
177 is_optional: true,
178 }
179 }
180
181 pub fn with_version(mut self, version: impl Into<String>) -> Self {
183 self.version = Some(version.into());
184 self
185 }
186
187 pub fn with_description(mut self, description: impl Into<String>) -> Self {
189 self.description = Some(description.into());
190 self
191 }
192
193 pub fn with_provider(mut self, provider: ServiceDescriptor) -> Self {
195 self.providers.push(provider);
196 self
197 }
198
199 pub fn with_providers(mut self, providers: Vec<ServiceDescriptor>) -> Self {
201 self.providers.extend(providers);
202 self
203 }
204
205 pub fn with_controller(mut self, controller: ControllerDescriptor) -> Self {
207 self.controllers.push(controller);
208 self
209 }
210
211 pub fn with_controllers(mut self, controllers: Vec<ControllerDescriptor>) -> Self {
213 self.controllers.extend(controllers);
214 self
215 }
216
217 pub fn with_imports(mut self, imports: Vec<String>) -> Self {
219 self.imports = imports;
220 self
221 }
222
223 pub fn with_exports(mut self, exports: Vec<String>) -> Self {
225 self.exports = exports;
226 self
227 }
228
229 pub fn with_dependencies(mut self, dependencies: Vec<String>) -> Self {
231 self.dependencies = dependencies;
232 self
233 }
234
235 pub fn with_optional(mut self, is_optional: bool) -> Self {
237 self.is_optional = is_optional;
238 self
239 }
240
241 pub fn service_count(&self) -> usize {
243 self.providers.len()
244 }
245
246 pub fn controller_count(&self) -> usize {
248 self.controllers.len()
249 }
250
251 pub fn has_exports(&self) -> bool {
253 !self.exports.is_empty()
254 }
255
256 pub fn has_imports(&self) -> bool {
258 !self.imports.is_empty()
259 }
260}
261
262pub trait ModuleAutoConfiguration {
264 fn module_descriptor() -> ModuleDescriptor;
266
267 fn auto_configure(container: &mut IocContainer) -> Result<(), ModuleError>;
269
270 fn configure_builder(builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> {
272 Ok(builder)
273 }
274}
275
276#[derive(Debug)]
278pub struct ModuleComposition {
279 pub modules: Vec<ModuleDescriptor>,
281 pub overrides: Vec<ServiceDescriptor>,
283 pub merged_descriptor: ModuleDescriptor,
285}
286
287impl ModuleComposition {
288 pub fn new() -> Self {
290 Self {
291 modules: Vec::new(),
292 overrides: Vec::new(),
293 merged_descriptor: ModuleDescriptor::new("Composed"),
294 }
295 }
296
297 pub fn with_module(mut self, descriptor: ModuleDescriptor) -> Self {
299 self.modules.push(descriptor);
300 self
301 }
302
303 pub fn with_overrides(mut self, overrides: Vec<ServiceDescriptor>) -> Self {
305 self.overrides = overrides;
306 self
307 }
308
309 pub fn compose(mut self) -> Result<ModuleDescriptor, ModuleError> {
311 let validator = ModuleDependencyValidator::new(&self.modules);
313 if let Err(validation_errors) = validator.validate() {
314 return Err(ModuleError::ConfigurationFailed {
315 message: format!(
316 "Module validation failed: {}",
317 validation_errors
318 .iter()
319 .map(|e| e.to_string())
320 .collect::<Vec<_>>()
321 .join("; ")
322 ),
323 });
324 }
325
326 let loading_order =
328 validator
329 .topological_sort()
330 .map_err(|e| ModuleError::ConfigurationFailed {
331 message: format!("Failed to determine module loading order: {}", e),
332 })?;
333
334 let mut final_descriptor = ModuleDescriptor::new("ComposedApplication");
336
337 let module_map: std::collections::HashMap<_, _> =
339 self.modules.iter().map(|m| (&m.name, m)).collect();
340
341 for module_name in &loading_order {
343 if let Some(module) = module_map.get(module_name) {
344 final_descriptor.providers.extend(module.providers.clone());
345 final_descriptor
346 .controllers
347 .extend(module.controllers.clone());
348 final_descriptor.imports.extend(module.imports.clone());
349 final_descriptor.exports.extend(module.exports.clone());
350 }
351 }
352
353 use std::collections::HashMap;
356 let override_map: HashMap<_, _> = self
357 .overrides
358 .iter()
359 .map(|s| ((s.service_type, s.name.clone()), s.clone()))
360 .collect();
361
362 final_descriptor.providers.retain(|p| {
364 let key = (p.service_type, p.name.clone());
365 !override_map.contains_key(&key)
366 });
367
368 final_descriptor
370 .providers
371 .extend(override_map.into_values());
372
373 self.merged_descriptor = final_descriptor.clone();
374 Ok(final_descriptor)
375 }
376
377 pub fn auto_configure_all(&self, _container: &mut IocContainer) -> Result<(), ModuleError> {
379 for _provider in &self.merged_descriptor.providers {
381 }
385
386 Ok(())
387 }
388}
389
390impl Default for ModuleComposition {
391 fn default() -> Self {
392 Self::new()
393 }
394}
395
396#[derive(Debug, Clone)]
398pub enum ModuleValidationError {
399 CircularImport { module: String, cycle: Vec<String> },
401 MissingExport {
403 importing_module: String,
404 target_module: String,
405 missing_service: String,
406 },
407 SelfImport { module: String },
409 DuplicateExport { module: String, service: String },
411}
412
413impl std::fmt::Display for ModuleValidationError {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 match self {
416 ModuleValidationError::CircularImport { module, cycle } => {
417 write!(
418 f,
419 "Circular import detected in module '{}': {}",
420 module,
421 cycle.join(" -> ")
422 )
423 }
424 ModuleValidationError::MissingExport {
425 importing_module,
426 target_module,
427 missing_service,
428 } => {
429 write!(
430 f,
431 "Module '{}' tries to import '{}' from '{}', but '{}' doesn't export it",
432 importing_module, missing_service, target_module, target_module
433 )
434 }
435 ModuleValidationError::SelfImport { module } => {
436 write!(f, "Module '{}' cannot import itself", module)
437 }
438 ModuleValidationError::DuplicateExport { module, service } => {
439 write!(
440 f,
441 "Module '{}' exports '{}' multiple times",
442 module, service
443 )
444 }
445 }
446 }
447}
448
449impl std::error::Error for ModuleValidationError {}
450
451#[derive(Debug)]
453pub struct ModuleDependencyValidator<'a> {
454 modules: &'a [ModuleDescriptor],
456}
457
458impl<'a> ModuleDependencyValidator<'a> {
459 pub fn new(modules: &'a [ModuleDescriptor]) -> Self {
461 Self { modules }
462 }
463
464 pub fn validate(&self) -> Result<(), Vec<ModuleValidationError>> {
467 let mut errors = Vec::new();
468
469 if let Err(export_errors) = self.validate_missing_exports() {
471 errors.extend(export_errors);
472 }
473
474 if let Err(self_import_errors) = self.validate_self_imports() {
476 errors.extend(self_import_errors);
477 }
478
479 if let Err(duplicate_errors) = self.validate_duplicate_exports() {
481 errors.extend(duplicate_errors);
482 }
483
484 if let Err(circular_error) = self.topological_sort() {
486 errors.push(circular_error);
487 }
488
489 if errors.is_empty() {
490 Ok(())
491 } else {
492 Err(errors)
493 }
494 }
495
496 fn validate_missing_exports(&self) -> Result<(), Vec<ModuleValidationError>> {
498 let mut errors = Vec::new();
499
500 let mut export_map: std::collections::HashMap<String, std::collections::HashSet<String>> =
502 std::collections::HashMap::new();
503
504 for module in self.modules {
505 export_map.insert(
506 module.name.clone(),
507 module.exports.iter().cloned().collect(),
508 );
509 }
510
511 for module in self.modules {
513 for import_module in &module.imports {
514 if let Some(exported_services) = export_map.get(import_module.as_str()) {
516 if exported_services.is_empty() {
519 continue;
522 }
523 } else {
524 errors.push(ModuleValidationError::MissingExport {
527 importing_module: module.name.clone(),
528 target_module: import_module.clone(),
529 missing_service: "*unknown*".to_string(),
530 });
531 }
532 }
533 }
534
535 if errors.is_empty() {
536 Ok(())
537 } else {
538 Err(errors)
539 }
540 }
541
542 fn validate_self_imports(&self) -> Result<(), Vec<ModuleValidationError>> {
544 let mut errors = Vec::new();
545
546 for module in self.modules {
547 if module.imports.contains(&module.name) {
548 errors.push(ModuleValidationError::SelfImport {
549 module: module.name.clone(),
550 });
551 }
552 }
553
554 if errors.is_empty() {
555 Ok(())
556 } else {
557 Err(errors)
558 }
559 }
560
561 fn validate_duplicate_exports(&self) -> Result<(), Vec<ModuleValidationError>> {
563 let mut errors = Vec::new();
564
565 for module in self.modules {
566 let mut seen_exports = std::collections::HashSet::new();
567
568 for export in &module.exports {
569 if !seen_exports.insert(export.clone()) {
570 errors.push(ModuleValidationError::DuplicateExport {
571 module: module.name.clone(),
572 service: export.clone(),
573 });
574 }
575 }
576 }
577
578 if errors.is_empty() {
579 Ok(())
580 } else {
581 Err(errors)
582 }
583 }
584
585 pub fn topological_sort(&self) -> Result<Vec<String>, ModuleValidationError> {
587 let mut visited = std::collections::HashSet::new();
588 let mut temp_visited = std::collections::HashSet::new();
589 let mut result = Vec::new();
590
591 for module in self.modules {
592 if !visited.contains(&module.name) {
593 if let Err(cycle) = self.topological_visit(
594 &module.name,
595 &mut visited,
596 &mut temp_visited,
597 &mut result,
598 ) {
599 return Err(ModuleValidationError::CircularImport {
600 module: module.name.clone(),
601 cycle,
602 });
603 }
604 }
605 }
606
607 Ok(result)
609 }
610
611 fn topological_visit(
613 &self,
614 module_name: &str,
615 visited: &mut std::collections::HashSet<String>,
616 temp_visited: &mut std::collections::HashSet<String>,
617 result: &mut Vec<String>,
618 ) -> Result<(), Vec<String>> {
619 if temp_visited.contains(module_name) {
620 return Err(vec![module_name.to_string()]);
621 }
622
623 if visited.contains(module_name) {
624 return Ok(());
625 }
626
627 temp_visited.insert(module_name.to_string());
628
629 if let Some(module) = self.modules.iter().find(|m| m.name == module_name) {
630 for import in &module.imports {
631 if let Err(mut cycle) =
632 self.topological_visit(import, visited, temp_visited, result)
633 {
634 cycle.insert(0, module_name.to_string());
635 return Err(cycle);
636 }
637 }
638 }
639
640 temp_visited.remove(module_name);
641 visited.insert(module_name.to_string());
642 result.push(module_name.to_string());
643
644 Ok(())
645 }
646}
647
648#[cfg(test)]
649mod tests {
650 use super::*;
651 use std::any::TypeId;
652
653 #[test]
654 fn test_service_descriptor_creation() {
655 let descriptor =
656 ServiceDescriptor::new::<String>("TestService", ServiceLifecycle::Singleton);
657
658 assert_eq!(descriptor.service_name, "TestService");
659 assert_eq!(descriptor.service_type, TypeId::of::<String>());
660 assert_eq!(descriptor.lifecycle, ServiceLifecycle::Singleton);
661 assert!(!descriptor.is_trait_service);
662 }
663
664 #[test]
665 fn test_trait_service_descriptor() {
666 let descriptor = ServiceDescriptor::trait_mapping::<String, Vec<u8>>(
667 "TraitService",
668 "Implementation",
669 ServiceLifecycle::Scoped,
670 );
671
672 assert!(descriptor.service_name.contains(" => "));
673 assert_eq!(descriptor.service_type, TypeId::of::<String>());
674 assert_eq!(
675 descriptor.implementation_type,
676 Some(TypeId::of::<Vec<u8>>())
677 );
678 assert!(descriptor.is_trait_service);
679 assert_eq!(descriptor.lifecycle, ServiceLifecycle::Scoped);
680 }
681
682 #[test]
683 fn test_controller_descriptor_creation() {
684 let descriptor = ControllerDescriptor::new::<String>("TestController")
685 .with_base_path("/api")
686 .with_middleware(vec!["auth".to_string(), "cors".to_string()]);
687
688 assert_eq!(descriptor.controller_name, "TestController");
689 assert_eq!(descriptor.base_path, Some("/api".to_string()));
690 assert_eq!(descriptor.middleware, vec!["auth", "cors"]);
691 }
692
693 #[test]
694 fn test_module_descriptor_builder() {
695 let provider = ServiceDescriptor::new::<String>("TestService", ServiceLifecycle::Singleton);
696 let controller = ControllerDescriptor::new::<Vec<u8>>("TestController");
697
698 let descriptor = ModuleDescriptor::new("TestModule")
699 .with_version("1.0.0")
700 .with_description("Test module for Epic 3")
701 .with_provider(provider)
702 .with_controller(controller)
703 .with_imports(vec!["DatabaseModule".to_string()])
704 .with_exports(vec!["TestService".to_string()])
705 .with_optional(false);
706
707 assert_eq!(descriptor.name, "TestModule");
708 assert_eq!(descriptor.version, Some("1.0.0".to_string()));
709 assert_eq!(descriptor.service_count(), 1);
710 assert_eq!(descriptor.controller_count(), 1);
711 assert!(descriptor.has_imports());
712 assert!(descriptor.has_exports());
713 assert!(!descriptor.is_optional);
714 }
715
716 #[test]
717 fn test_module_composition() {
718 let module1 =
719 ModuleDescriptor::new("Module1").with_provider(ServiceDescriptor::new::<String>(
720 "Service1",
721 ServiceLifecycle::Singleton,
722 ));
723
724 let module2 =
725 ModuleDescriptor::new("Module2").with_provider(ServiceDescriptor::new::<Vec<u8>>(
726 "Service2",
727 ServiceLifecycle::Scoped,
728 ));
729
730 let composition = ModuleComposition::new()
731 .with_module(module1)
732 .with_module(module2);
733
734 let result = composition.compose().unwrap();
735
736 assert_eq!(result.name, "ComposedApplication");
737 assert_eq!(result.service_count(), 2);
738 }
739
740 #[test]
741 fn test_module_composition_with_overrides() {
742 let module =
743 ModuleDescriptor::new("TestModule").with_provider(ServiceDescriptor::new::<String>(
744 "OriginalService",
745 ServiceLifecycle::Singleton,
746 ));
747
748 let override_service =
749 ServiceDescriptor::new::<String>("OverrideService", ServiceLifecycle::Transient);
750
751 let composition = ModuleComposition::new()
752 .with_module(module)
753 .with_overrides(vec![override_service]);
754
755 let result = composition.compose().unwrap();
756
757 assert_eq!(result.service_count(), 1);
759 assert_eq!(result.providers[0].service_name, "OverrideService");
760 assert_eq!(result.providers[0].lifecycle, ServiceLifecycle::Transient);
761 }
762
763 #[test]
764 fn test_module_validation_circular_imports() {
765 let module_a = ModuleDescriptor::new("ModuleA").with_imports(vec!["ModuleB".to_string()]);
766
767 let module_b = ModuleDescriptor::new("ModuleB").with_imports(vec!["ModuleC".to_string()]);
768
769 let module_c = ModuleDescriptor::new("ModuleC").with_imports(vec!["ModuleA".to_string()]); let modules = vec![module_a, module_b, module_c];
772 let validator = ModuleDependencyValidator::new(&modules);
773 let result = validator.validate();
774
775 assert!(result.is_err());
776 let errors = result.unwrap_err();
777 assert_eq!(errors.len(), 1);
778
779 match &errors[0] {
780 ModuleValidationError::CircularImport { module, cycle } => {
781 assert!(module == "ModuleA" || module == "ModuleB" || module == "ModuleC");
782 assert!(cycle.len() >= 3);
783 }
784 _ => panic!("Expected CircularImport error"),
785 }
786 }
787
788 #[test]
789 fn test_module_validation_self_import() {
790 let module =
791 ModuleDescriptor::new("SelfModule").with_imports(vec!["SelfModule".to_string()]);
792
793 let modules = vec![module];
794 let validator = ModuleDependencyValidator::new(&modules);
795 let result = validator.validate();
796
797 assert!(result.is_err());
798 let errors = result.unwrap_err();
799
800 assert!(!errors.is_empty());
802
803 let has_self_import = errors.iter().any(|e| {
805 matches!(e,
806 ModuleValidationError::SelfImport { module } if module == "SelfModule"
807 )
808 });
809 assert!(has_self_import, "Should have SelfImport error");
810 }
811
812 #[test]
813 fn test_module_validation_missing_exports() {
814 let module_a =
815 ModuleDescriptor::new("ModuleA").with_imports(vec!["NonExistentModule".to_string()]);
816
817 let modules = vec![module_a];
818 let validator = ModuleDependencyValidator::new(&modules);
819 let result = validator.validate();
820
821 assert!(result.is_err());
822 let errors = result.unwrap_err();
823 assert_eq!(errors.len(), 1);
824
825 match &errors[0] {
826 ModuleValidationError::MissingExport {
827 importing_module,
828 target_module,
829 ..
830 } => {
831 assert_eq!(importing_module, "ModuleA");
832 assert_eq!(target_module, "NonExistentModule");
833 }
834 _ => panic!("Expected MissingExport error"),
835 }
836 }
837
838 #[test]
839 fn test_module_validation_duplicate_exports() {
840 let module = ModuleDescriptor::new("TestModule")
841 .with_exports(vec!["Service1".to_string(), "Service1".to_string()]);
842
843 let modules = vec![module];
844 let validator = ModuleDependencyValidator::new(&modules);
845 let result = validator.validate();
846
847 assert!(result.is_err());
848 let errors = result.unwrap_err();
849 assert_eq!(errors.len(), 1);
850
851 match &errors[0] {
852 ModuleValidationError::DuplicateExport { module, service } => {
853 assert_eq!(module, "TestModule");
854 assert_eq!(service, "Service1");
855 }
856 _ => panic!("Expected DuplicateExport error"),
857 }
858 }
859
860 #[test]
861 fn test_module_validation_success() {
862 let module_a = ModuleDescriptor::new("ModuleA").with_exports(vec!["ServiceA".to_string()]);
863
864 let module_b = ModuleDescriptor::new("ModuleB")
865 .with_imports(vec!["ModuleA".to_string()])
866 .with_exports(vec!["ServiceB".to_string()]);
867
868 let modules = vec![module_a, module_b];
869 let validator = ModuleDependencyValidator::new(&modules);
870 let result = validator.validate();
871
872 assert!(result.is_ok());
873 }
874
875 #[test]
876 fn test_module_topological_sort() {
877 let module_a = ModuleDescriptor::new("ModuleA"); let module_b = ModuleDescriptor::new("ModuleB").with_imports(vec!["ModuleA".to_string()]); let module_c = ModuleDescriptor::new("ModuleC").with_imports(vec!["ModuleB".to_string()]); let modules = vec![module_c, module_a, module_b];
884 let validator = ModuleDependencyValidator::new(&modules);
885 let sorted = validator.topological_sort().unwrap();
886
887 let a_pos = sorted.iter().position(|m| m == "ModuleA").unwrap();
890 let b_pos = sorted.iter().position(|m| m == "ModuleB").unwrap();
891 let c_pos = sorted.iter().position(|m| m == "ModuleC").unwrap();
892
893 assert!(a_pos < b_pos, "ModuleA should come before ModuleB");
894 assert!(b_pos < c_pos, "ModuleB should come before ModuleC");
895 }
896
897 #[test]
898 fn test_module_validation_error_display() {
899 let error = ModuleValidationError::CircularImport {
900 module: "TestModule".to_string(),
901 cycle: vec![
902 "A".to_string(),
903 "B".to_string(),
904 "C".to_string(),
905 "A".to_string(),
906 ],
907 };
908
909 let error_string = format!("{}", error);
910 assert!(error_string.contains("Circular import detected in module 'TestModule'"));
911 assert!(error_string.contains("A -> B -> C -> A"));
912 }
913}