Skip to main content

sklears_compose/modular_framework/
dependency_management.rs

1//! Dependency Management System
2//!
3//! This module provides comprehensive dependency resolution, compatibility checking,
4//! and dependency graph management for modular components including circular dependency
5//! detection, version constraint solving, and dependency injection patterns.
6
7use sklears_core::error::{Result as SklResult, SklearsError};
8use std::collections::{HashMap, HashSet};
9use std::sync::{Arc, RwLock};
10use thiserror::Error;
11
12use super::component_framework::{ComponentDependency, PluggableComponent};
13
14/// Dependency resolver for managing component dependencies
15///
16/// Provides dependency resolution, topological sorting, circular dependency detection,
17/// and version constraint satisfaction for complex component dependency graphs.
18#[derive(Debug)]
19pub struct DependencyResolver {
20    /// Component dependency graph
21    dependency_graph: Arc<RwLock<DependencyGraph>>,
22    /// Version constraint solver
23    version_solver: VersionConstraintSolver,
24    /// Dependency injection registry
25    injection_registry: Arc<RwLock<DependencyInjectionRegistry>>,
26    /// Resolution configuration
27    config: DependencyResolutionConfig,
28    /// Resolution cache
29    resolution_cache: Arc<RwLock<HashMap<String, ResolutionResult>>>,
30    /// Resolver statistics
31    stats: Arc<RwLock<DependencyStatistics>>,
32}
33
34impl DependencyResolver {
35    /// Create a new dependency resolver
36    #[must_use]
37    pub fn new() -> Self {
38        Self {
39            dependency_graph: Arc::new(RwLock::new(DependencyGraph::new())),
40            version_solver: VersionConstraintSolver::new(),
41            injection_registry: Arc::new(RwLock::new(DependencyInjectionRegistry::new())),
42            config: DependencyResolutionConfig::default(),
43            resolution_cache: Arc::new(RwLock::new(HashMap::new())),
44            stats: Arc::new(RwLock::new(DependencyStatistics::new())),
45        }
46    }
47
48    /// Add component dependencies to the graph
49    pub fn add_component_dependencies(
50        &self,
51        component_id: &str,
52        component_type: &str,
53        version: &str,
54        dependencies: Vec<ComponentDependency>,
55    ) -> SklResult<()> {
56        let mut graph = self
57            .dependency_graph
58            .write()
59            .unwrap_or_else(|e| e.into_inner());
60        let mut stats = self.stats.write().unwrap_or_else(|e| e.into_inner());
61
62        let node = DependencyNode {
63            component_id: component_id.to_string(),
64            component_type: component_type.to_string(),
65            version: version.to_string(),
66            dependencies: dependencies.clone(),
67            resolved_dependencies: HashMap::new(),
68            dependency_state: DependencyState::Unresolved,
69        };
70
71        graph.add_node(component_id.to_string(), node);
72
73        // Add edges for dependencies
74        for dependency in dependencies {
75            graph.add_edge(component_id.to_string(), dependency.component_type.clone());
76            stats.total_dependencies += 1;
77        }
78
79        stats.total_components += 1;
80        Ok(())
81    }
82
83    /// Resolve dependencies for a component
84    pub fn resolve_dependencies(&self, component_id: &str) -> SklResult<ResolutionResult> {
85        let mut stats = self.stats.write().unwrap_or_else(|e| e.into_inner());
86        stats.resolution_attempts += 1;
87
88        // Check cache first
89        {
90            let cache = self
91                .resolution_cache
92                .read()
93                .unwrap_or_else(|e| e.into_inner());
94            if let Some(cached_result) = cache.get(component_id) {
95                if !self.is_cache_expired(&cached_result.resolution_time) {
96                    stats.cache_hits += 1;
97                    return Ok(cached_result.clone());
98                }
99            }
100        }
101
102        stats.cache_misses += 1;
103
104        // Perform resolution
105        let resolution_order = self.topological_sort(component_id)?;
106        let version_constraints = self.collect_version_constraints(&resolution_order)?;
107        let resolved_versions = self.version_solver.solve_constraints(version_constraints)?;
108
109        let result = ResolutionResult {
110            component_id: component_id.to_string(),
111            resolution_order,
112            resolved_versions,
113            resolution_time: std::time::Instant::now(),
114            dependency_conflicts: self.detect_conflicts(component_id)?,
115            optional_dependencies: self.collect_optional_dependencies(component_id)?,
116        };
117
118        // Cache the result
119        {
120            let mut cache = self
121                .resolution_cache
122                .write()
123                .unwrap_or_else(|e| e.into_inner());
124            cache.insert(component_id.to_string(), result.clone());
125        }
126
127        // Update dependency states
128        self.update_dependency_states(component_id, &result)?;
129
130        stats.successful_resolutions += 1;
131        Ok(result)
132    }
133
134    /// Perform topological sort for dependency resolution order
135    pub fn topological_sort(&self, component_id: &str) -> SklResult<Vec<String>> {
136        let graph = self
137            .dependency_graph
138            .read()
139            .unwrap_or_else(|e| e.into_inner());
140        let mut visited = HashSet::new();
141        let mut visiting = HashSet::new();
142        let mut order = Vec::new();
143
144        self.topological_sort_visit(
145            &graph,
146            component_id,
147            &mut visited,
148            &mut visiting,
149            &mut order,
150        )?;
151
152        Ok(order)
153    }
154
155    /// Detect circular dependencies
156    pub fn detect_circular_dependencies(&self) -> SklResult<Vec<CircularDependency>> {
157        let graph = self
158            .dependency_graph
159            .read()
160            .unwrap_or_else(|e| e.into_inner());
161        let mut circular_deps = Vec::new();
162        let mut visited = HashSet::new();
163        let mut visiting = HashSet::new();
164
165        for component_id in graph.nodes.keys() {
166            if !visited.contains(component_id) {
167                if let Err(DependencyError::CircularDependency { cycle }) = self.detect_cycles(
168                    &graph,
169                    component_id,
170                    &mut visited,
171                    &mut visiting,
172                    &mut Vec::new(),
173                ) {
174                    circular_deps.push(CircularDependency {
175                        cycle_components: cycle,
176                        detection_time: std::time::Instant::now(),
177                    });
178                }
179            }
180        }
181
182        Ok(circular_deps)
183    }
184
185    /// Check dependency compatibility
186    pub fn check_compatibility(
187        &self,
188        component_a: &str,
189        component_b: &str,
190    ) -> SklResult<CompatibilityResult> {
191        let graph = self
192            .dependency_graph
193            .read()
194            .unwrap_or_else(|e| e.into_inner());
195
196        let node_a = graph.nodes.get(component_a).ok_or_else(|| {
197            SklearsError::InvalidInput(format!("Component {component_a} not found"))
198        })?;
199        let node_b = graph.nodes.get(component_b).ok_or_else(|| {
200            SklearsError::InvalidInput(format!("Component {component_b} not found"))
201        })?;
202
203        let mut compatibility_issues = Vec::new();
204
205        // Check version compatibility
206        for dep in &node_a.dependencies {
207            if dep.component_type == node_b.component_type
208                && !self
209                    .version_solver
210                    .is_version_compatible(&node_b.version, &dep.version_requirement)
211            {
212                compatibility_issues.push(CompatibilityIssue {
213                    issue_type: CompatibilityIssueType::VersionMismatch,
214                    component_a: component_a.to_string(),
215                    component_b: component_b.to_string(),
216                    description: format!(
217                        "Version mismatch: {} requires {} but {} has version {}",
218                        component_a, dep.version_requirement, component_b, node_b.version
219                    ),
220                });
221            }
222        }
223
224        // Check capability compatibility
225        self.check_capability_compatibility(node_a, node_b, &mut compatibility_issues)?;
226
227        Ok(CompatibilityResult {
228            compatible: compatibility_issues.is_empty(),
229            issues: compatibility_issues,
230            compatibility_score: self.calculate_compatibility_score(node_a, node_b),
231        })
232    }
233
234    /// Register dependency injection provider
235    pub fn register_injection_provider<T: 'static>(
236        &self,
237        provider: Box<dyn DependencyProvider<T>>,
238    ) -> SklResult<()> {
239        let mut registry = self
240            .injection_registry
241            .write()
242            .unwrap_or_else(|e| e.into_inner());
243        registry.register_provider(std::any::TypeId::of::<T>(), provider)?;
244        Ok(())
245    }
246
247    /// Inject dependencies into a component
248    pub fn inject_dependencies(&self, component: &mut dyn PluggableComponent) -> SklResult<()> {
249        let registry = self
250            .injection_registry
251            .read()
252            .unwrap_or_else(|e| e.into_inner());
253
254        // Get component's dependency requirements
255        let dependencies = component.dependencies();
256
257        for dependency in dependencies {
258            if let Some(_provider) = registry.get_provider(&dependency.component_type) {
259                // TODO: Implement proper downcasting from Any to DependencyProvider
260                // For now, skip injection since this is a placeholder implementation
261            } else if !dependency.optional {
262                return Err(SklearsError::InvalidInput(format!(
263                    "Required dependency provider not found: {}",
264                    dependency.component_type
265                )));
266            }
267        }
268
269        Ok(())
270    }
271
272    /// Get dependency statistics
273    #[must_use]
274    pub fn get_statistics(&self) -> DependencyStatistics {
275        let stats = self.stats.read().unwrap_or_else(|e| e.into_inner());
276        stats.clone()
277    }
278
279    /// Clear resolution cache
280    pub fn clear_cache(&self) {
281        let mut cache = self
282            .resolution_cache
283            .write()
284            .unwrap_or_else(|e| e.into_inner());
285        cache.clear();
286    }
287
288    /// Private helper methods
289    fn topological_sort_visit(
290        &self,
291        graph: &DependencyGraph,
292        component_id: &str,
293        visited: &mut HashSet<String>,
294        visiting: &mut HashSet<String>,
295        order: &mut Vec<String>,
296    ) -> SklResult<()> {
297        if visiting.contains(component_id) {
298            return Err(DependencyError::CircularDependency {
299                cycle: vec![component_id.to_string()],
300            }
301            .into());
302        }
303
304        if visited.contains(component_id) {
305            return Ok(());
306        }
307
308        visiting.insert(component_id.to_string());
309
310        if let Some(edges) = graph.edges.get(component_id) {
311            for dependency in edges {
312                self.topological_sort_visit(graph, dependency, visited, visiting, order)?;
313            }
314        }
315
316        visiting.remove(component_id);
317        visited.insert(component_id.to_string());
318        order.push(component_id.to_string());
319
320        Ok(())
321    }
322
323    fn detect_cycles(
324        &self,
325        graph: &DependencyGraph,
326        component_id: &str,
327        visited: &mut HashSet<String>,
328        visiting: &mut HashSet<String>,
329        path: &mut Vec<String>,
330    ) -> Result<(), DependencyError> {
331        if visiting.contains(component_id) {
332            let cycle_start = path.iter().position(|id| id == component_id).unwrap_or(0);
333            let cycle = path[cycle_start..].to_vec();
334            return Err(DependencyError::CircularDependency { cycle });
335        }
336
337        if visited.contains(component_id) {
338            return Ok(());
339        }
340
341        visiting.insert(component_id.to_string());
342        path.push(component_id.to_string());
343
344        if let Some(edges) = graph.edges.get(component_id) {
345            for dependency in edges {
346                self.detect_cycles(graph, dependency, visited, visiting, path)?;
347            }
348        }
349
350        visiting.remove(component_id);
351        visited.insert(component_id.to_string());
352        path.pop();
353
354        Ok(())
355    }
356
357    fn collect_version_constraints(
358        &self,
359        components: &[String],
360    ) -> SklResult<Vec<VersionConstraint>> {
361        let graph = self
362            .dependency_graph
363            .read()
364            .unwrap_or_else(|e| e.into_inner());
365        let mut constraints = Vec::new();
366
367        for component_id in components {
368            if let Some(node) = graph.nodes.get(component_id) {
369                for dependency in &node.dependencies {
370                    constraints.push(VersionConstraint {
371                        component_type: dependency.component_type.clone(),
372                        constraint: dependency.version_requirement.clone(),
373                        required_by: component_id.clone(),
374                    });
375                }
376            }
377        }
378
379        Ok(constraints)
380    }
381
382    fn detect_conflicts(&self, component_id: &str) -> SklResult<Vec<DependencyConflict>> {
383        // Implementation for conflict detection
384        Ok(Vec::new())
385    }
386
387    fn collect_optional_dependencies(&self, component_id: &str) -> SklResult<Vec<String>> {
388        let graph = self
389            .dependency_graph
390            .read()
391            .unwrap_or_else(|e| e.into_inner());
392        let mut optional_deps = Vec::new();
393
394        if let Some(node) = graph.nodes.get(component_id) {
395            for dependency in &node.dependencies {
396                if dependency.optional {
397                    optional_deps.push(dependency.component_type.clone());
398                }
399            }
400        }
401
402        Ok(optional_deps)
403    }
404
405    fn update_dependency_states(
406        &self,
407        component_id: &str,
408        result: &ResolutionResult,
409    ) -> SklResult<()> {
410        let mut graph = self
411            .dependency_graph
412            .write()
413            .unwrap_or_else(|e| e.into_inner());
414
415        if let Some(node) = graph.nodes.get_mut(component_id) {
416            node.dependency_state = if result.dependency_conflicts.is_empty() {
417                DependencyState::Resolved
418            } else {
419                DependencyState::Conflicted
420            };
421        }
422
423        Ok(())
424    }
425
426    fn is_cache_expired(&self, resolution_time: &std::time::Instant) -> bool {
427        resolution_time.elapsed() > self.config.cache_expiry_duration
428    }
429
430    fn check_capability_compatibility(
431        &self,
432        node_a: &DependencyNode,
433        node_b: &DependencyNode,
434        issues: &mut Vec<CompatibilityIssue>,
435    ) -> SklResult<()> {
436        // Check if required capabilities are satisfied
437        for dependency in &node_a.dependencies {
438            if dependency.component_type == node_b.component_type {
439                // In a real implementation, this would check if node_b provides
440                // the capabilities required by dependency.required_capabilities
441            }
442        }
443        Ok(())
444    }
445
446    fn calculate_compatibility_score(
447        &self,
448        node_a: &DependencyNode,
449        node_b: &DependencyNode,
450    ) -> f64 {
451        // Simple compatibility scoring based on version proximity and capability overlap
452        1.0 // Placeholder implementation
453    }
454}
455
456/// Dependency graph representation
457#[derive(Debug)]
458pub struct DependencyGraph {
459    /// Component nodes
460    pub nodes: HashMap<String, DependencyNode>,
461    /// Dependency edges (`component_id` -> dependencies)
462    pub edges: HashMap<String, Vec<String>>,
463}
464
465impl DependencyGraph {
466    #[must_use]
467    pub fn new() -> Self {
468        Self {
469            nodes: HashMap::new(),
470            edges: HashMap::new(),
471        }
472    }
473
474    pub fn add_node(&mut self, component_id: String, node: DependencyNode) {
475        self.nodes.insert(component_id, node);
476    }
477
478    pub fn add_edge(&mut self, from: String, to: String) {
479        self.edges.entry(from).or_default().push(to);
480    }
481}
482
483/// Dependency node in the graph
484#[derive(Debug, Clone)]
485pub struct DependencyNode {
486    /// Component identifier
487    pub component_id: String,
488    /// Component type
489    pub component_type: String,
490    /// Component version
491    pub version: String,
492    /// Component dependencies
493    pub dependencies: Vec<ComponentDependency>,
494    /// Resolved dependency mappings
495    pub resolved_dependencies: HashMap<String, String>,
496    /// Current dependency state
497    pub dependency_state: DependencyState,
498}
499
500/// Dependency resolution states
501#[derive(Debug, Clone, PartialEq, Eq)]
502pub enum DependencyState {
503    /// Dependencies not yet resolved
504    Unresolved,
505    /// Dependencies resolved successfully
506    Resolved,
507    /// Dependency conflicts detected
508    Conflicted,
509    /// Dependencies partially resolved
510    PartiallyResolved,
511}
512
513/// Version constraint solver
514pub struct VersionConstraintSolver {
515    /// Supported constraint operators
516    constraint_operators: HashMap<String, Box<dyn Fn(&str, &str) -> bool + Send + Sync>>,
517}
518
519impl VersionConstraintSolver {
520    #[must_use]
521    pub fn new() -> Self {
522        let mut operators = HashMap::new();
523
524        // Add constraint operators
525        operators.insert(
526            "=".to_string(),
527            Box::new(|version: &str, constraint: &str| version == constraint)
528                as Box<dyn Fn(&str, &str) -> bool + Send + Sync>,
529        );
530
531        operators.insert(
532            ">=".to_string(),
533            Box::new(|version: &str, constraint: &str| {
534                Self::compare_versions(version, constraint) >= 0
535            }) as Box<dyn Fn(&str, &str) -> bool + Send + Sync>,
536        );
537
538        Self {
539            constraint_operators: operators,
540        }
541    }
542
543    pub fn solve_constraints(
544        &self,
545        constraints: Vec<VersionConstraint>,
546    ) -> SklResult<HashMap<String, String>> {
547        let mut resolved_versions = HashMap::new();
548        let mut component_constraints: HashMap<String, Vec<String>> = HashMap::new();
549
550        // Group constraints by component type
551        for constraint in constraints {
552            component_constraints
553                .entry(constraint.component_type.clone())
554                .or_default()
555                .push(constraint.constraint);
556        }
557
558        // Resolve versions for each component type
559        for (component_type, version_constraints) in component_constraints {
560            let resolved_version = self.resolve_version_constraints(version_constraints)?;
561            resolved_versions.insert(component_type, resolved_version);
562        }
563
564        Ok(resolved_versions)
565    }
566
567    #[must_use]
568    pub fn is_version_compatible(&self, version: &str, constraint: &str) -> bool {
569        // Simple version compatibility check
570        // In a real implementation, this would parse constraint operators
571        VersionConstraintSolver::compare_versions(version, constraint) >= 0
572    }
573
574    fn resolve_version_constraints(&self, constraints: Vec<String>) -> SklResult<String> {
575        // For now, return the highest version constraint
576        // In a real implementation, this would solve the constraint satisfaction problem
577        Ok(constraints
578            .into_iter()
579            .max()
580            .unwrap_or_else(|| "1.0.0".to_string()))
581    }
582
583    fn compare_versions(version_a: &str, version_b: &str) -> i32 {
584        let parts_a: Vec<u32> = version_a
585            .split('.')
586            .filter_map(|s| s.parse().ok())
587            .collect();
588        let parts_b: Vec<u32> = version_b
589            .split('.')
590            .filter_map(|s| s.parse().ok())
591            .collect();
592
593        for i in 0..std::cmp::max(parts_a.len(), parts_b.len()) {
594            let a = parts_a.get(i).copied().unwrap_or(0);
595            let b = parts_b.get(i).copied().unwrap_or(0);
596
597            match a.cmp(&b) {
598                std::cmp::Ordering::Less => return -1,
599                std::cmp::Ordering::Greater => return 1,
600                std::cmp::Ordering::Equal => {}
601            }
602        }
603
604        0
605    }
606}
607
608impl std::fmt::Debug for VersionConstraintSolver {
609    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
610        f.debug_struct("VersionConstraintSolver")
611            .field(
612                "constraint_operators",
613                &format!("<{} operators>", self.constraint_operators.len()),
614            )
615            .finish()
616    }
617}
618
619/// Version constraint
620#[derive(Debug, Clone)]
621pub struct VersionConstraint {
622    /// Component type
623    pub component_type: String,
624    /// Version constraint string
625    pub constraint: String,
626    /// Component that requires this constraint
627    pub required_by: String,
628}
629
630/// Dependency injection registry
631#[derive(Debug)]
632pub struct DependencyInjectionRegistry {
633    /// Registered providers by type
634    providers: HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync>>,
635    /// Provider metadata
636    provider_metadata: HashMap<String, ProviderMetadata>,
637}
638
639impl DependencyInjectionRegistry {
640    #[must_use]
641    pub fn new() -> Self {
642        Self {
643            providers: HashMap::new(),
644            provider_metadata: HashMap::new(),
645        }
646    }
647
648    pub fn register_provider<T: 'static>(
649        &mut self,
650        type_id: std::any::TypeId,
651        provider: Box<dyn DependencyProvider<T>>,
652    ) -> SklResult<()> {
653        self.providers.insert(type_id, Box::new(provider));
654        Ok(())
655    }
656
657    #[must_use]
658    pub fn get_provider(&self, component_type: &str) -> Option<&dyn std::any::Any> {
659        // This would look up providers by component type
660        // Placeholder implementation
661        None
662    }
663}
664
665/// Dependency provider trait
666pub trait DependencyProvider<T>: Send + Sync {
667    /// Inject dependency into component
668    fn inject(&self, component: &mut dyn PluggableComponent) -> SklResult<()>;
669
670    /// Get provider metadata
671    fn metadata(&self) -> ProviderMetadata;
672}
673
674/// Provider metadata
675#[derive(Debug, Clone)]
676pub struct ProviderMetadata {
677    /// Provider name
678    pub name: String,
679    /// Provided type
680    pub provided_type: String,
681    /// Provider version
682    pub version: String,
683}
684
685/// Dependency resolution result
686#[derive(Debug, Clone)]
687pub struct ResolutionResult {
688    /// Component identifier
689    pub component_id: String,
690    /// Resolution order
691    pub resolution_order: Vec<String>,
692    /// Resolved versions
693    pub resolved_versions: HashMap<String, String>,
694    /// Resolution timestamp
695    pub resolution_time: std::time::Instant,
696    /// Detected conflicts
697    pub dependency_conflicts: Vec<DependencyConflict>,
698    /// Optional dependencies
699    pub optional_dependencies: Vec<String>,
700}
701
702/// Dependency conflict
703#[derive(Debug, Clone)]
704pub struct DependencyConflict {
705    /// Conflicting components
706    pub components: Vec<String>,
707    /// Conflict type
708    pub conflict_type: ConflictType,
709    /// Conflict description
710    pub description: String,
711}
712
713/// Conflict types
714#[derive(Debug, Clone)]
715pub enum ConflictType {
716    /// Version constraint conflict
717    VersionConflict,
718    /// Capability conflict
719    CapabilityConflict,
720    /// Resource conflict
721    ResourceConflict,
722    /// Circular dependency
723    CircularDependency,
724}
725
726/// Circular dependency detection result
727#[derive(Debug, Clone)]
728pub struct CircularDependency {
729    /// Components in the cycle
730    pub cycle_components: Vec<String>,
731    /// Detection timestamp
732    pub detection_time: std::time::Instant,
733}
734
735/// Compatibility check result
736#[derive(Debug, Clone)]
737pub struct CompatibilityResult {
738    /// Whether components are compatible
739    pub compatible: bool,
740    /// Compatibility issues
741    pub issues: Vec<CompatibilityIssue>,
742    /// Compatibility score (0.0 to 1.0)
743    pub compatibility_score: f64,
744}
745
746/// Compatibility issue
747#[derive(Debug, Clone)]
748pub struct CompatibilityIssue {
749    /// Issue type
750    pub issue_type: CompatibilityIssueType,
751    /// First component
752    pub component_a: String,
753    /// Second component
754    pub component_b: String,
755    /// Issue description
756    pub description: String,
757}
758
759/// Compatibility issue types
760#[derive(Debug, Clone)]
761pub enum CompatibilityIssueType {
762    /// Version mismatch
763    VersionMismatch,
764    /// Missing capability
765    MissingCapability,
766    /// Resource conflict
767    ResourceConflict,
768    /// Configuration conflict
769    ConfigurationConflict,
770}
771
772/// Dependency resolution configuration
773#[derive(Debug, Clone)]
774pub struct DependencyResolutionConfig {
775    /// Enable circular dependency detection
776    pub enable_circular_detection: bool,
777    /// Maximum resolution depth
778    pub max_resolution_depth: usize,
779    /// Cache expiry duration
780    pub cache_expiry_duration: std::time::Duration,
781    /// Allow version downgrades
782    pub allow_version_downgrades: bool,
783    /// Strict version matching
784    pub strict_version_matching: bool,
785}
786
787impl Default for DependencyResolutionConfig {
788    fn default() -> Self {
789        Self {
790            enable_circular_detection: true,
791            max_resolution_depth: 50,
792            cache_expiry_duration: std::time::Duration::from_secs(300),
793            allow_version_downgrades: false,
794            strict_version_matching: false,
795        }
796    }
797}
798
799/// Dependency statistics
800#[derive(Debug, Clone)]
801pub struct DependencyStatistics {
802    /// Total components in dependency graph
803    pub total_components: u64,
804    /// Total dependencies
805    pub total_dependencies: u64,
806    /// Resolution attempts
807    pub resolution_attempts: u64,
808    /// Successful resolutions
809    pub successful_resolutions: u64,
810    /// Cache hits
811    pub cache_hits: u64,
812    /// Cache misses
813    pub cache_misses: u64,
814    /// Circular dependencies detected
815    pub circular_dependencies_detected: u64,
816    /// Average resolution time
817    pub average_resolution_time: std::time::Duration,
818}
819
820impl DependencyStatistics {
821    #[must_use]
822    pub fn new() -> Self {
823        Self {
824            total_components: 0,
825            total_dependencies: 0,
826            resolution_attempts: 0,
827            successful_resolutions: 0,
828            cache_hits: 0,
829            cache_misses: 0,
830            circular_dependencies_detected: 0,
831            average_resolution_time: std::time::Duration::from_secs(0),
832        }
833    }
834
835    /// Get cache hit rate
836    #[must_use]
837    pub fn cache_hit_rate(&self) -> f64 {
838        if self.resolution_attempts == 0 {
839            0.0
840        } else {
841            self.cache_hits as f64 / self.resolution_attempts as f64
842        }
843    }
844
845    /// Get resolution success rate
846    #[must_use]
847    pub fn resolution_success_rate(&self) -> f64 {
848        if self.resolution_attempts == 0 {
849            0.0
850        } else {
851            self.successful_resolutions as f64 / self.resolution_attempts as f64
852        }
853    }
854}
855
856/// Dependency management errors
857#[derive(Debug, Error)]
858pub enum DependencyError {
859    #[error("Circular dependency detected: {cycle:?}")]
860    CircularDependency { cycle: Vec<String> },
861
862    #[error("Version constraint conflict: {0}")]
863    VersionConstraintConflict(String),
864
865    #[error("Dependency not found: {0}")]
866    DependencyNotFound(String),
867
868    #[error("Resolution failed: {0}")]
869    ResolutionFailed(String),
870
871    #[error("Injection failed: {0}")]
872    InjectionFailed(String),
873}
874
875impl From<DependencyError> for SklearsError {
876    fn from(err: DependencyError) -> Self {
877        SklearsError::InvalidInput(err.to_string())
878    }
879}
880
881impl Default for DependencyResolver {
882    fn default() -> Self {
883        Self::new()
884    }
885}
886
887impl Default for DependencyGraph {
888    fn default() -> Self {
889        Self::new()
890    }
891}
892
893impl Default for VersionConstraintSolver {
894    fn default() -> Self {
895        Self::new()
896    }
897}
898
899impl Default for DependencyInjectionRegistry {
900    fn default() -> Self {
901        Self::new()
902    }
903}
904
905impl Default for DependencyStatistics {
906    fn default() -> Self {
907        Self::new()
908    }
909}
910
911#[allow(non_snake_case)]
912#[cfg(test)]
913mod tests {
914    use super::*;
915
916    #[test]
917    fn test_dependency_resolver_creation() {
918        let resolver = DependencyResolver::new();
919        let stats = resolver.get_statistics();
920        assert_eq!(stats.total_components, 0);
921        assert_eq!(stats.total_dependencies, 0);
922    }
923
924    #[test]
925    fn test_version_constraint_solver() {
926        let solver = VersionConstraintSolver::new();
927
928        assert!(solver.is_version_compatible("1.2.0", "1.0.0"));
929        assert!(!solver.is_version_compatible("1.0.0", "1.2.0"));
930        assert!(solver.is_version_compatible("1.0.0", "1.0.0"));
931    }
932
933    #[test]
934    fn test_dependency_graph() {
935        let mut graph = DependencyGraph::new();
936
937        let node = DependencyNode {
938            component_id: "comp1".to_string(),
939            component_type: "type1".to_string(),
940            version: "1.0.0".to_string(),
941            dependencies: Vec::new(),
942            resolved_dependencies: HashMap::new(),
943            dependency_state: DependencyState::Unresolved,
944        };
945
946        graph.add_node("comp1".to_string(), node);
947        graph.add_edge("comp1".to_string(), "comp2".to_string());
948
949        assert!(graph.nodes.contains_key("comp1"));
950        assert_eq!(
951            graph
952                .edges
953                .get("comp1")
954                .expect("operation should succeed")
955                .len(),
956            1
957        );
958    }
959
960    #[test]
961    fn test_circular_dependency_detection() {
962        let resolver = DependencyResolver::new();
963
964        // Add components with circular dependencies
965        let dep1 = ComponentDependency {
966            component_type: "comp2".to_string(),
967            version_requirement: "1.0.0".to_string(),
968            optional: false,
969            required_capabilities: Vec::new(),
970        };
971
972        let dep2 = ComponentDependency {
973            component_type: "comp1".to_string(),
974            version_requirement: "1.0.0".to_string(),
975            optional: false,
976            required_capabilities: Vec::new(),
977        };
978
979        resolver
980            .add_component_dependencies("comp1", "type1", "1.0.0", vec![dep1])
981            .unwrap_or_default();
982        resolver
983            .add_component_dependencies("comp2", "type2", "1.0.0", vec![dep2])
984            .unwrap_or_default();
985
986        let circular_deps = resolver.detect_circular_dependencies().unwrap_or_default();
987        assert!(!circular_deps.is_empty());
988    }
989
990    #[test]
991    fn test_topological_sort() {
992        let resolver = DependencyResolver::new();
993
994        // Add components in dependency order: comp3 -> comp2 -> comp1
995        let dep1 = ComponentDependency {
996            component_type: "comp2".to_string(),
997            version_requirement: "1.0.0".to_string(),
998            optional: false,
999            required_capabilities: Vec::new(),
1000        };
1001
1002        let dep2 = ComponentDependency {
1003            component_type: "comp3".to_string(),
1004            version_requirement: "1.0.0".to_string(),
1005            optional: false,
1006            required_capabilities: Vec::new(),
1007        };
1008
1009        resolver
1010            .add_component_dependencies("comp1", "type1", "1.0.0", vec![dep1])
1011            .unwrap_or_default();
1012        resolver
1013            .add_component_dependencies("comp2", "type2", "1.0.0", vec![dep2])
1014            .unwrap_or_default();
1015        resolver
1016            .add_component_dependencies("comp3", "type3", "1.0.0", vec![])
1017            .unwrap_or_default();
1018
1019        let order = resolver.topological_sort("comp1").unwrap_or_default();
1020
1021        // Should be in order: comp3, comp2, comp1
1022        assert_eq!(order.len(), 3);
1023        assert_eq!(order[0], "comp3");
1024        assert_eq!(order[1], "comp2");
1025        assert_eq!(order[2], "comp1");
1026    }
1027}