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