1use std::collections::{HashMap, HashSet, VecDeque};
14use std::time::{Duration, Instant};
15
16use crate::container::IocContainer;
17use crate::errors::CoreError;
18use crate::modules::ModuleDescriptor;
19
20#[derive(Debug, Clone)]
22pub enum ModuleRuntimeError {
23 CircularDependency { cycle: Vec<String>, message: String },
25 MissingDependency {
27 module: String,
28 missing_dependency: String,
29 message: String,
30 },
31 InitializationFailed {
33 module: String,
34 error: String,
35 phase: String,
36 },
37 LifecycleOperationFailed {
39 module: String,
40 operation: String,
41 error: String,
42 },
43 ConfigurationConflict {
45 module1: String,
46 module2: String,
47 conflict: String,
48 },
49 ValidationFailed {
51 module: String,
52 validation_errors: Vec<String>,
53 },
54}
55
56impl std::fmt::Display for ModuleRuntimeError {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 match self {
59 ModuleRuntimeError::CircularDependency { cycle, message } => {
60 write!(
61 f,
62 "Circular dependency: {} -> {}",
63 cycle.join(" -> "),
64 message
65 )
66 }
67 ModuleRuntimeError::MissingDependency {
68 module,
69 missing_dependency,
70 message,
71 } => {
72 write!(
73 f,
74 "Module '{}' missing dependency '{}': {}",
75 module, missing_dependency, message
76 )
77 }
78 ModuleRuntimeError::InitializationFailed {
79 module,
80 error,
81 phase,
82 } => {
83 write!(
84 f,
85 "Module '{}' initialization failed in phase '{}': {}",
86 module, phase, error
87 )
88 }
89 ModuleRuntimeError::LifecycleOperationFailed {
90 module,
91 operation,
92 error,
93 } => {
94 write!(
95 f,
96 "Module '{}' lifecycle operation '{}' failed: {}",
97 module, operation, error
98 )
99 }
100 ModuleRuntimeError::ConfigurationConflict {
101 module1,
102 module2,
103 conflict,
104 } => {
105 write!(
106 f,
107 "Configuration conflict between modules '{}' and '{}': {}",
108 module1, module2, conflict
109 )
110 }
111 ModuleRuntimeError::ValidationFailed {
112 module,
113 validation_errors,
114 } => {
115 write!(
116 f,
117 "Module '{}' validation failed: {}",
118 module,
119 validation_errors.join("; ")
120 )
121 }
122 }
123 }
124}
125
126impl std::error::Error for ModuleRuntimeError {}
127
128impl From<ModuleRuntimeError> for CoreError {
130 fn from(err: ModuleRuntimeError) -> Self {
131 CoreError::InvalidServiceDescriptor {
132 message: err.to_string(),
133 }
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum ModuleState {
140 Registered,
142 ResolvingDependencies,
144 Configuring,
146 Initializing,
148 Ready,
150 Failed(String),
152 ShuttingDown,
154 Shutdown,
156}
157
158#[derive(Debug, Clone)]
160pub struct ModuleRuntimeInfo {
161 pub descriptor: ModuleDescriptor,
163 pub state: ModuleState,
165 pub load_order: Option<usize>,
167 pub init_duration: Option<Duration>,
169 pub config_duration: Option<Duration>,
171 pub errors: Vec<String>,
173 pub health_status: HealthStatus,
175 pub last_health_check: Option<Instant>,
177}
178
179impl ModuleRuntimeInfo {
180 pub fn new(descriptor: ModuleDescriptor) -> Self {
182 Self {
183 descriptor,
184 state: ModuleState::Registered,
185 load_order: None,
186 init_duration: None,
187 config_duration: None,
188 errors: Vec::new(),
189 health_status: HealthStatus::Unknown,
190 last_health_check: None,
191 }
192 }
193
194 pub fn is_ready(&self) -> bool {
196 self.state == ModuleState::Ready
197 }
198
199 pub fn has_failed(&self) -> bool {
201 matches!(self.state, ModuleState::Failed(_))
202 }
203
204 pub fn add_error(&mut self, error: String) {
206 self.errors.push(error);
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
212pub enum HealthStatus {
213 Unknown,
215 Healthy,
217 Degraded,
219 Unhealthy,
221}
222
223#[derive(Debug, Default, Clone)]
225pub struct ModulePerformanceMetrics {
226 pub total_modules: usize,
228 pub topological_sort_duration: Duration,
230 pub dependency_resolution_duration: Duration,
232 pub configuration_duration: Duration,
234 pub initialization_duration: Duration,
236 pub avg_init_time_per_module: Duration,
238 pub slowest_module: Option<String>,
240 pub slowest_init_time: Duration,
242}
243
244pub struct ModuleRuntime {
246 modules: HashMap<String, ModuleRuntimeInfo>,
248 dependency_graph: HashMap<String, Vec<String>>,
250 load_order: Vec<String>,
252 metrics: ModulePerformanceMetrics,
254 lifecycle_hooks: HashMap<String, Box<dyn ModuleLifecycleHook>>,
256 #[allow(dead_code)]
258 health_check_config: HealthCheckConfig,
259}
260
261#[derive(Debug, Clone)]
263pub struct HealthCheckConfig {
264 pub interval: Duration,
266 pub timeout: Duration,
268 pub enabled: bool,
270}
271
272impl Default for HealthCheckConfig {
273 fn default() -> Self {
274 Self {
275 interval: Duration::from_secs(30),
276 timeout: Duration::from_secs(5),
277 enabled: true,
278 }
279 }
280}
281
282pub trait ModuleLifecycleHook: Send + Sync {
284 fn before_init(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
286 let _ = module_name;
287 Ok(())
288 }
289
290 fn after_init(&self, module_name: &str, duration: Duration) -> Result<(), ModuleRuntimeError> {
292 let _ = (module_name, duration);
293 Ok(())
294 }
295
296 fn on_init_failure(&self, module_name: &str, error: &ModuleRuntimeError) {
298 let _ = (module_name, error);
299 }
300
301 fn before_shutdown(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
303 let _ = module_name;
304 Ok(())
305 }
306
307 fn after_shutdown(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
309 let _ = module_name;
310 Ok(())
311 }
312
313 fn health_check(&self, module_name: &str) -> Result<HealthStatus, ModuleRuntimeError> {
315 let _ = module_name;
316 Ok(HealthStatus::Unknown)
317 }
318}
319
320impl ModuleRuntime {
321 pub fn new() -> Self {
323 Self {
324 modules: HashMap::new(),
325 dependency_graph: HashMap::new(),
326 load_order: Vec::new(),
327 metrics: ModulePerformanceMetrics::default(),
328 lifecycle_hooks: HashMap::new(),
329 health_check_config: HealthCheckConfig::default(),
330 }
331 }
332
333 pub fn with_health_config(health_config: HealthCheckConfig) -> Self {
335 Self {
336 modules: HashMap::new(),
337 dependency_graph: HashMap::new(),
338 load_order: Vec::new(),
339 metrics: ModulePerformanceMetrics::default(),
340 lifecycle_hooks: HashMap::new(),
341 health_check_config: health_config,
342 }
343 }
344
345 pub fn register_module(
347 &mut self,
348 descriptor: ModuleDescriptor,
349 ) -> Result<(), ModuleRuntimeError> {
350 let module_name = descriptor.name.clone();
351
352 if self.modules.contains_key(&module_name) {
354 return Err(ModuleRuntimeError::ConfigurationConflict {
355 module1: module_name.clone(),
356 module2: module_name,
357 conflict: "Module already registered".to_string(),
358 });
359 }
360
361 self.dependency_graph
363 .insert(module_name.clone(), descriptor.dependencies.clone());
364
365 let runtime_info = ModuleRuntimeInfo::new(descriptor);
367 self.modules.insert(module_name, runtime_info);
368
369 Ok(())
370 }
371
372 pub fn register_modules(
374 &mut self,
375 descriptors: Vec<ModuleDescriptor>,
376 ) -> Result<(), ModuleRuntimeError> {
377 for descriptor in descriptors {
378 self.register_module(descriptor)?;
379 }
380 Ok(())
381 }
382
383 pub fn add_lifecycle_hook<H: ModuleLifecycleHook + 'static>(
385 &mut self,
386 module_name: String,
387 hook: H,
388 ) {
389 self.lifecycle_hooks.insert(module_name, Box::new(hook));
390 }
391
392 pub fn calculate_load_order(&mut self) -> Result<Vec<String>, ModuleRuntimeError> {
394 let start_time = Instant::now();
395
396 let sorted_modules = self.topological_sort()?;
397
398 for (index, module_name) in sorted_modules.iter().enumerate() {
400 if let Some(module_info) = self.modules.get_mut(module_name) {
401 module_info.load_order = Some(index);
402 }
403 }
404
405 self.load_order = sorted_modules.clone();
406 self.metrics.topological_sort_duration = start_time.elapsed();
407
408 Ok(sorted_modules)
409 }
410
411 pub fn load_order(&self) -> &[String] {
413 &self.load_order
414 }
415
416 fn topological_sort(&self) -> Result<Vec<String>, ModuleRuntimeError> {
418 let mut in_degree: HashMap<String, usize> = HashMap::new();
419 let mut graph: HashMap<String, Vec<String>> = HashMap::new();
420
421 for module_name in self.modules.keys() {
423 in_degree.insert(module_name.clone(), 0);
424 graph.insert(module_name.clone(), Vec::new());
425 }
426
427 for (module_name, dependencies) in &self.dependency_graph {
429 for dependency in dependencies {
430 if !self.modules.contains_key(dependency) {
432 return Err(ModuleRuntimeError::MissingDependency {
433 module: module_name.clone(),
434 missing_dependency: dependency.clone(),
435 message: "Dependency not registered".to_string(),
436 });
437 }
438
439 graph.get_mut(dependency).unwrap().push(module_name.clone());
441 *in_degree.get_mut(module_name).unwrap() += 1;
442 }
443 }
444
445 let mut queue: VecDeque<String> = in_degree
446 .iter()
447 .filter(|(_, °ree)| degree == 0)
448 .map(|(name, _)| name.clone())
449 .collect();
450
451 let mut result = Vec::new();
452
453 while let Some(current) = queue.pop_front() {
454 result.push(current.clone());
455
456 for dependent in &graph[¤t] {
458 let degree = in_degree.get_mut(dependent).unwrap();
459 *degree -= 1;
460 if *degree == 0 {
461 queue.push_back(dependent.clone());
462 }
463 }
464 }
465
466 if result.len() != self.modules.len() {
468 let cycle = self.find_cycle()?;
470 return Err(ModuleRuntimeError::CircularDependency {
471 cycle: cycle.clone(),
472 message: format!(
473 "Circular dependency detected: {}. This creates an infinite loop during module initialization.",
474 cycle.join(" -> ")
475 ),
476 });
477 }
478
479 Ok(result)
480 }
481
482 fn find_cycle(&self) -> Result<Vec<String>, ModuleRuntimeError> {
484 let mut visited = HashSet::new();
485 let mut rec_stack = HashSet::new();
486 let mut path = Vec::new();
487
488 for module_name in self.modules.keys() {
489 if !visited.contains(module_name) {
490 if let Some(cycle) =
491 self.dfs_cycle_detection(module_name, &mut visited, &mut rec_stack, &mut path)?
492 {
493 return Ok(cycle);
494 }
495 }
496 }
497
498 Ok(vec!["unknown".to_string()])
500 }
501
502 fn dfs_cycle_detection(
504 &self,
505 current: &str,
506 visited: &mut HashSet<String>,
507 rec_stack: &mut HashSet<String>,
508 path: &mut Vec<String>,
509 ) -> Result<Option<Vec<String>>, ModuleRuntimeError> {
510 visited.insert(current.to_string());
511 rec_stack.insert(current.to_string());
512 path.push(current.to_string());
513
514 if let Some(dependencies) = self.dependency_graph.get(current) {
515 for dep in dependencies {
516 if !visited.contains(dep) {
517 if let Some(cycle) = self.dfs_cycle_detection(dep, visited, rec_stack, path)? {
518 return Ok(Some(cycle));
519 }
520 } else if rec_stack.contains(dep) {
521 if let Some(start_idx) = path.iter().position(|x| x == dep) {
523 let mut cycle = path[start_idx..].to_vec();
524 cycle.push(dep.clone()); return Ok(Some(cycle));
526 }
527 }
528 }
529 }
530
531 rec_stack.remove(current);
532 path.pop();
533 Ok(None)
534 }
535
536 pub async fn resolve_dependencies(
538 &mut self,
539 container: &mut IocContainer,
540 ) -> Result<(), ModuleRuntimeError> {
541 let start_time = Instant::now();
542
543 if self.load_order.is_empty() {
545 self.calculate_load_order()?;
546 }
547
548 for module_name in &self.load_order.clone() {
550 self.resolve_module_dependencies(module_name, container)
551 .await?;
552 }
553
554 self.metrics.dependency_resolution_duration = start_time.elapsed();
555 Ok(())
556 }
557
558 async fn resolve_module_dependencies(
560 &mut self,
561 module_name: &str,
562 container: &mut IocContainer,
563 ) -> Result<(), ModuleRuntimeError> {
564 if let Some(module_info) = self.modules.get_mut(module_name) {
566 module_info.state = ModuleState::ResolvingDependencies;
567 }
568
569 let dependencies = self
570 .dependency_graph
571 .get(module_name)
572 .cloned()
573 .unwrap_or_default();
574
575 for dep_name in &dependencies {
577 let dep_info = self.modules.get(dep_name).ok_or_else(|| {
578 ModuleRuntimeError::MissingDependency {
579 module: module_name.to_string(),
580 missing_dependency: dep_name.clone(),
581 message: "Dependency module not found in runtime".to_string(),
582 }
583 })?;
584
585 if matches!(dep_info.state, ModuleState::Failed(_)) {
586 return Err(ModuleRuntimeError::InitializationFailed {
587 module: module_name.to_string(),
588 error: format!(
589 "Dependency '{}' failed initialization (state: {:?})",
590 dep_name, dep_info.state
591 ),
592 phase: "dependency_resolution".to_string(),
593 });
594 }
595 }
596
597 self.configure_module(module_name, container).await?;
599
600 Ok(())
601 }
602
603 async fn configure_module(
605 &mut self,
606 module_name: &str,
607 container: &mut IocContainer,
608 ) -> Result<(), ModuleRuntimeError> {
609 let start_time = Instant::now();
610
611 if let Some(module_info) = self.modules.get_mut(module_name) {
613 module_info.state = ModuleState::Configuring;
614 }
615
616 let descriptor = self
618 .modules
619 .get(module_name)
620 .ok_or_else(|| ModuleRuntimeError::MissingDependency {
621 module: module_name.to_string(),
622 missing_dependency: "module_descriptor".to_string(),
623 message: "Module not found".to_string(),
624 })?
625 .descriptor
626 .clone();
627
628 self.configure_module_services(&descriptor, container)
630 .await?;
631
632 self.configure_module_controllers(&descriptor, container)
634 .await?;
635
636 let config_duration = start_time.elapsed();
638 if let Some(module_info) = self.modules.get_mut(module_name) {
639 module_info.config_duration = Some(config_duration);
640 module_info.state = ModuleState::Initializing;
641 }
642
643 Ok(())
644 }
645
646 async fn configure_module_services(
648 &self,
649 descriptor: &ModuleDescriptor,
650 _container: &mut IocContainer,
651 ) -> Result<(), ModuleRuntimeError> {
652 for service in &descriptor.providers {
653 match (service.implementation_type, &service.name) {
654 (Some(_), Some(name)) if service.is_trait_service => {
656 tracing::warn!(
659 "Trait service '{}' with name '{}' requires token-based binding (not yet fully integrated)",
660 service.service_name, name
661 );
662 }
663 (None, Some(name)) => {
665 tracing::warn!(
668 "Named concrete service '{}' with name '{}' requires enhanced binding support",
669 service.service_name, name
670 );
671 }
672 (Some(_), None) if service.is_trait_service => {
674 tracing::warn!(
676 "Trait service '{}' requires token-based binding (not yet fully integrated)",
677 service.service_name
678 );
679 }
680 (None, None) => {
682 tracing::info!(
685 "Concrete service '{}' registered (runtime binding not yet implemented)",
686 service.service_name
687 );
688 }
689 _ => {
691 tracing::warn!(
692 "Unknown service configuration for '{}' - skipping",
693 service.service_name
694 );
695 }
696 }
697 }
698
699 Ok(())
700 }
701
702 async fn configure_module_controllers(
704 &self,
705 descriptor: &ModuleDescriptor,
706 _container: &mut IocContainer,
707 ) -> Result<(), ModuleRuntimeError> {
708 for controller in &descriptor.controllers {
709 tracing::info!(
712 "Controller '{}' registered (HTTP routing integration pending)",
713 controller.controller_name
714 );
715 }
716
717 Ok(())
718 }
719
720 pub async fn initialize_all_modules(
722 &mut self,
723 container: &IocContainer,
724 ) -> Result<(), ModuleRuntimeError> {
725 let start_time = Instant::now();
726
727 for module_name in &self.load_order.clone() {
728 self.initialize_module(module_name, container).await?;
729 }
730
731 self.metrics.initialization_duration = start_time.elapsed();
732 self.calculate_performance_metrics();
733
734 Ok(())
735 }
736
737 async fn initialize_module(
739 &mut self,
740 module_name: &str,
741 _container: &IocContainer,
742 ) -> Result<(), ModuleRuntimeError> {
743 let init_start = Instant::now();
744
745 if let Some(hook) = self.lifecycle_hooks.get(module_name) {
747 hook.before_init(module_name)?;
748 }
749
750 if let Some(module_info) = self.modules.get_mut(module_name) {
752 module_info.state = ModuleState::Initializing;
753 }
754
755 tokio::time::sleep(Duration::from_millis(10)).await;
758
759 let init_duration = init_start.elapsed();
760
761 if let Some(module_info) = self.modules.get_mut(module_name) {
763 module_info.state = ModuleState::Ready;
764 module_info.init_duration = Some(init_duration);
765 }
766
767 if let Some(hook) = self.lifecycle_hooks.get(module_name) {
769 if let Err(e) = hook.after_init(module_name, init_duration) {
770 if let Some(module_info) = self.modules.get_mut(module_name) {
772 module_info.state = ModuleState::Failed(e.to_string());
773 module_info.add_error(e.to_string());
774 }
775 return Err(e);
776 }
777 }
778
779 Ok(())
780 }
781
782 pub async fn shutdown_all_modules(&mut self) -> Result<(), ModuleRuntimeError> {
784 let mut shutdown_order = self.load_order.clone();
785 shutdown_order.reverse();
786
787 for module_name in shutdown_order {
788 self.shutdown_module(&module_name).await?;
789 }
790
791 Ok(())
792 }
793
794 async fn shutdown_module(&mut self, module_name: &str) -> Result<(), ModuleRuntimeError> {
796 if let Some(hook) = self.lifecycle_hooks.get(module_name) {
798 hook.before_shutdown(module_name)?;
799 }
800
801 if let Some(module_info) = self.modules.get_mut(module_name) {
803 module_info.state = ModuleState::ShuttingDown;
804 }
805
806 tokio::time::sleep(Duration::from_millis(5)).await;
808
809 if let Some(module_info) = self.modules.get_mut(module_name) {
811 module_info.state = ModuleState::Shutdown;
812 }
813
814 if let Some(hook) = self.lifecycle_hooks.get(module_name) {
816 hook.after_shutdown(module_name)?;
817 }
818
819 Ok(())
820 }
821
822 pub async fn health_check_all_modules(
824 &mut self,
825 ) -> Result<HashMap<String, HealthStatus>, ModuleRuntimeError> {
826 let mut health_results = HashMap::new();
827 let check_time = Instant::now();
828
829 for module_name in &self.load_order.clone() {
830 let health_status = if let Some(hook) = self.lifecycle_hooks.get(module_name) {
831 hook.health_check(module_name)
832 .unwrap_or(HealthStatus::Unknown)
833 } else {
834 match self.modules.get(module_name).map(|m| &m.state) {
836 Some(ModuleState::Ready) => HealthStatus::Healthy,
837 Some(ModuleState::Failed(_)) => HealthStatus::Unhealthy,
838 _ => HealthStatus::Unknown,
839 }
840 };
841
842 if let Some(module_info) = self.modules.get_mut(module_name) {
844 module_info.health_status = health_status.clone();
845 module_info.last_health_check = Some(check_time);
846 }
847
848 health_results.insert(module_name.clone(), health_status);
849 }
850
851 Ok(health_results)
852 }
853
854 fn calculate_performance_metrics(&mut self) {
856 self.metrics.total_modules = self.modules.len();
857
858 if self.metrics.total_modules > 0 {
859 let total_init_time: Duration =
860 self.modules.values().filter_map(|m| m.init_duration).sum();
861
862 self.metrics.avg_init_time_per_module =
863 total_init_time / self.metrics.total_modules as u32;
864
865 let slowest = self
867 .modules
868 .iter()
869 .filter_map(|(name, info)| info.init_duration.map(|d| (name, d)))
870 .max_by_key(|(_, duration)| *duration);
871
872 if let Some((name, duration)) = slowest {
873 self.metrics.slowest_module = Some(name.clone());
874 self.metrics.slowest_init_time = duration;
875 }
876 }
877 }
878
879 pub fn get_module_info(&self, module_name: &str) -> Option<&ModuleRuntimeInfo> {
881 self.modules.get(module_name)
882 }
883
884 pub fn get_all_module_info(&self) -> &HashMap<String, ModuleRuntimeInfo> {
886 &self.modules
887 }
888
889 pub fn get_load_order(&self) -> &[String] {
891 &self.load_order
892 }
893
894 pub fn get_performance_metrics(&self) -> &ModulePerformanceMetrics {
896 &self.metrics
897 }
898
899 pub fn validate_runtime_state(&self) -> Result<(), Vec<ModuleRuntimeError>> {
901 let mut errors = Vec::new();
902
903 for (name, info) in &self.modules {
905 match &info.state {
906 ModuleState::Failed(err) => {
907 errors.push(ModuleRuntimeError::InitializationFailed {
908 module: name.clone(),
909 error: err.clone(),
910 phase: "runtime_validation".to_string(),
911 });
912 }
913 ModuleState::ResolvingDependencies
914 | ModuleState::Configuring
915 | ModuleState::Initializing => {
916 errors.push(ModuleRuntimeError::InitializationFailed {
917 module: name.clone(),
918 error: "Module stuck in intermediate state".to_string(),
919 phase: format!("{:?}", info.state),
920 });
921 }
922 _ => {}
923 }
924
925 if info.errors.len() > 5 {
927 errors.push(ModuleRuntimeError::ValidationFailed {
928 module: name.clone(),
929 validation_errors: vec![format!("Module has {} errors", info.errors.len())],
930 });
931 }
932 }
933
934 if self.load_order.len() != self.modules.len() {
936 errors.push(ModuleRuntimeError::ValidationFailed {
937 module: "runtime".to_string(),
938 validation_errors: vec![format!(
939 "Load order length ({}) doesn't match module count ({})",
940 self.load_order.len(),
941 self.modules.len()
942 )],
943 });
944 }
945
946 if errors.is_empty() {
947 Ok(())
948 } else {
949 Err(errors)
950 }
951 }
952
953 #[cfg(test)]
955 pub fn modules_mut(&mut self) -> &mut HashMap<String, ModuleRuntimeInfo> {
956 &mut self.modules
957 }
958
959 pub fn get_runtime_statistics(&self) -> ModuleRuntimeStatistics {
961 let mut stats = ModuleRuntimeStatistics::default();
962
963 stats.total_modules = self.modules.len();
964
965 for info in self.modules.values() {
966 match &info.state {
967 ModuleState::Registered => stats.registered_modules += 1,
968 ModuleState::ResolvingDependencies => stats.resolving_modules += 1,
969 ModuleState::Configuring => stats.configuring_modules += 1,
970 ModuleState::Initializing => stats.initializing_modules += 1,
971 ModuleState::Ready => stats.ready_modules += 1,
972 ModuleState::Failed(_) => stats.failed_modules += 1,
973 ModuleState::ShuttingDown => stats.shutting_down_modules += 1,
974 ModuleState::Shutdown => stats.shutdown_modules += 1,
975 }
976
977 match &info.health_status {
978 HealthStatus::Healthy => stats.healthy_modules += 1,
979 HealthStatus::Degraded => stats.degraded_modules += 1,
980 HealthStatus::Unhealthy => stats.unhealthy_modules += 1,
981 HealthStatus::Unknown => stats.unknown_health_modules += 1,
982 }
983 }
984
985 stats.performance_metrics = self.metrics.clone();
986
987 stats
988 }
989}
990
991impl Default for ModuleRuntime {
992 fn default() -> Self {
993 Self::new()
994 }
995}
996
997impl std::fmt::Debug for ModuleRuntime {
998 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
999 f.debug_struct("ModuleRuntime")
1000 .field("modules", &self.modules)
1001 .field("dependency_graph", &self.dependency_graph)
1002 .field("load_order", &self.load_order)
1003 .field("metrics", &self.metrics)
1004 .field("lifecycle_hooks", &format!("{} hooks", self.lifecycle_hooks.len()))
1005 .field("health_check_config", &self.health_check_config)
1006 .finish()
1007 }
1008}
1009
1010#[derive(Debug, Default, Clone)]
1012pub struct ModuleRuntimeStatistics {
1013 pub total_modules: usize,
1014 pub registered_modules: usize,
1015 pub resolving_modules: usize,
1016 pub configuring_modules: usize,
1017 pub initializing_modules: usize,
1018 pub ready_modules: usize,
1019 pub failed_modules: usize,
1020 pub shutting_down_modules: usize,
1021 pub shutdown_modules: usize,
1022 pub healthy_modules: usize,
1023 pub degraded_modules: usize,
1024 pub unhealthy_modules: usize,
1025 pub unknown_health_modules: usize,
1026 pub performance_metrics: ModulePerformanceMetrics,
1027}
1028
1029pub struct DefaultLifecycleHook;
1031
1032impl ModuleLifecycleHook for DefaultLifecycleHook {
1033 fn before_init(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
1034 tracing::info!("Starting initialization of module '{}'", module_name);
1035 Ok(())
1036 }
1037
1038 fn after_init(&self, module_name: &str, duration: Duration) -> Result<(), ModuleRuntimeError> {
1039 tracing::info!(
1040 "Module '{}' initialized successfully in {:?}",
1041 module_name,
1042 duration
1043 );
1044 Ok(())
1045 }
1046
1047 fn on_init_failure(&self, module_name: &str, error: &ModuleRuntimeError) {
1048 tracing::error!("Module '{}' initialization failed: {}", module_name, error);
1049 }
1050
1051 fn health_check(&self, module_name: &str) -> Result<HealthStatus, ModuleRuntimeError> {
1052 let _ = module_name;
1054 Ok(HealthStatus::Healthy)
1055 }
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060 use super::*;
1061
1062 fn create_test_module(name: &str, dependencies: Vec<String>) -> ModuleDescriptor {
1063 ModuleDescriptor::new(name)
1064 .with_dependencies(dependencies)
1065 .with_description(format!("Test module {}", name))
1066 }
1067
1068 #[tokio::test]
1069 async fn test_topological_sorting_simple() {
1070 let mut runtime = ModuleRuntime::new();
1071
1072 runtime
1074 .register_module(create_test_module("A", vec![]))
1075 .unwrap();
1076 runtime
1077 .register_module(create_test_module("B", vec!["A".to_string()]))
1078 .unwrap();
1079 runtime
1080 .register_module(create_test_module("C", vec!["B".to_string()]))
1081 .unwrap();
1082
1083 let load_order = runtime.calculate_load_order().unwrap();
1084
1085 assert_eq!(load_order, vec!["A", "B", "C"]);
1086 }
1087
1088 #[tokio::test]
1089 async fn test_circular_dependency_detection() {
1090 let mut runtime = ModuleRuntime::new();
1091
1092 runtime
1094 .register_module(create_test_module("A", vec!["C".to_string()]))
1095 .unwrap();
1096 runtime
1097 .register_module(create_test_module("B", vec!["A".to_string()]))
1098 .unwrap();
1099 runtime
1100 .register_module(create_test_module("C", vec!["B".to_string()]))
1101 .unwrap();
1102
1103 let result = runtime.calculate_load_order();
1104
1105 assert!(result.is_err());
1106 match result.unwrap_err() {
1107 ModuleRuntimeError::CircularDependency { cycle, .. } => {
1108 assert!(cycle.len() >= 3);
1109 }
1110 _ => panic!("Expected CircularDependency error"),
1111 }
1112 }
1113
1114 #[tokio::test]
1115 async fn test_missing_dependency_detection() {
1116 let mut runtime = ModuleRuntime::new();
1117
1118 runtime
1119 .register_module(create_test_module("A", vec!["NonExistent".to_string()]))
1120 .unwrap();
1121
1122 let result = runtime.calculate_load_order();
1123
1124 assert!(result.is_err());
1125 match result.unwrap_err() {
1126 ModuleRuntimeError::MissingDependency {
1127 module,
1128 missing_dependency,
1129 ..
1130 } => {
1131 assert_eq!(module, "A");
1132 assert_eq!(missing_dependency, "NonExistent");
1133 }
1134 _ => panic!("Expected MissingDependency error"),
1135 }
1136 }
1137
1138 #[tokio::test]
1139 async fn test_complex_dependency_graph() {
1140 let mut runtime = ModuleRuntime::new();
1141
1142 runtime
1149 .register_module(create_test_module("A", vec![]))
1150 .unwrap();
1151 runtime
1152 .register_module(create_test_module("B", vec!["A".to_string()]))
1153 .unwrap();
1154 runtime
1155 .register_module(create_test_module("C", vec!["A".to_string()]))
1156 .unwrap();
1157 runtime
1158 .register_module(create_test_module(
1159 "D",
1160 vec!["B".to_string(), "C".to_string()],
1161 ))
1162 .unwrap();
1163 runtime
1164 .register_module(create_test_module("E", vec!["D".to_string()]))
1165 .unwrap();
1166
1167 let load_order = runtime.calculate_load_order().unwrap();
1168
1169 assert_eq!(load_order[0], "A");
1171
1172 let a_pos = load_order.iter().position(|x| x == "A").unwrap();
1174 let b_pos = load_order.iter().position(|x| x == "B").unwrap();
1175 let c_pos = load_order.iter().position(|x| x == "C").unwrap();
1176 let d_pos = load_order.iter().position(|x| x == "D").unwrap();
1177 let e_pos = load_order.iter().position(|x| x == "E").unwrap();
1178
1179 assert!(a_pos < b_pos);
1180 assert!(a_pos < c_pos);
1181 assert!(b_pos < d_pos);
1182 assert!(c_pos < d_pos);
1183 assert!(d_pos < e_pos);
1184 }
1185
1186 #[tokio::test]
1187 async fn test_module_lifecycle_hooks() {
1188 let mut runtime = ModuleRuntime::new();
1189 runtime
1190 .register_module(create_test_module("TestModule", vec![]))
1191 .unwrap();
1192
1193 runtime.add_lifecycle_hook("TestModule".to_string(), DefaultLifecycleHook);
1195
1196 runtime.calculate_load_order().unwrap();
1198
1199 let mut container = IocContainer::new();
1201 container.build().unwrap();
1202
1203 let result = runtime.initialize_all_modules(&container).await;
1205 assert!(result.is_ok());
1206
1207 let module_info = runtime.get_module_info("TestModule").unwrap();
1209 assert_eq!(module_info.state, ModuleState::Ready);
1210 assert!(module_info.init_duration.is_some());
1211 }
1212
1213 #[tokio::test]
1214 async fn test_health_checks() {
1215 let mut runtime = ModuleRuntime::new();
1216 runtime
1217 .register_module(create_test_module("TestModule", vec![]))
1218 .unwrap();
1219 runtime.add_lifecycle_hook("TestModule".to_string(), DefaultLifecycleHook);
1220
1221 runtime.calculate_load_order().unwrap();
1222
1223 let mut container = IocContainer::new();
1224 container.build().unwrap();
1225 runtime.initialize_all_modules(&container).await.unwrap();
1226
1227 let health_results = runtime.health_check_all_modules().await.unwrap();
1229
1230 assert_eq!(health_results.len(), 1);
1231 assert_eq!(health_results["TestModule"], HealthStatus::Healthy);
1232 }
1233
1234 #[tokio::test]
1235 async fn test_runtime_validation() {
1236 let mut runtime = ModuleRuntime::new();
1237 runtime
1238 .register_module(create_test_module("TestModule", vec![]))
1239 .unwrap();
1240
1241 runtime.calculate_load_order().unwrap();
1242
1243 let mut container = IocContainer::new();
1244 container.build().unwrap();
1245 runtime.initialize_all_modules(&container).await.unwrap();
1246
1247 let validation_result = runtime.validate_runtime_state();
1249 assert!(validation_result.is_ok());
1250 }
1251
1252 #[tokio::test]
1253 async fn test_performance_metrics() {
1254 let mut runtime = ModuleRuntime::new();
1255
1256 for i in 0..10 {
1257 runtime
1258 .register_module(create_test_module(&format!("Module{}", i), vec![]))
1259 .unwrap();
1260 }
1261
1262 runtime.calculate_load_order().unwrap();
1263
1264 let mut container = IocContainer::new();
1265 container.build().unwrap();
1266 runtime.initialize_all_modules(&container).await.unwrap();
1267
1268 let metrics = runtime.get_performance_metrics();
1269
1270 assert_eq!(metrics.total_modules, 10);
1271 assert!(metrics.initialization_duration > Duration::ZERO);
1272 assert!(metrics.avg_init_time_per_module > Duration::ZERO);
1273 }
1274
1275 #[tokio::test]
1276 async fn test_shutdown_order() {
1277 let mut runtime = ModuleRuntime::new();
1278
1279 runtime
1280 .register_module(create_test_module("A", vec![]))
1281 .unwrap();
1282 runtime
1283 .register_module(create_test_module("B", vec!["A".to_string()]))
1284 .unwrap();
1285 runtime
1286 .register_module(create_test_module("C", vec!["B".to_string()]))
1287 .unwrap();
1288
1289 runtime.calculate_load_order().unwrap();
1290
1291 let mut container = IocContainer::new();
1292 container.build().unwrap();
1293 runtime.initialize_all_modules(&container).await.unwrap();
1294
1295 runtime.shutdown_all_modules().await.unwrap();
1297
1298 for info in runtime.get_all_module_info().values() {
1300 assert_eq!(info.state, ModuleState::Shutdown);
1301 }
1302 }
1303}