dmsc/core/module_sorter.rs
1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//! http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Module Sorter
19//!
20//! This module provides functionality for sorting modules based on their dependencies and priorities.
21//! It uses topological sort to handle dependencies and sorts modules by priority within the same dependency level.
22//!
23//! ## Key Components
24//!
25//! - **sort_modules**: Function that performs topological sorting of modules
26//!
27//! ## Design Principles
28//!
29//! 1. **Topological Sort**: Uses Kahn's algorithm for dependency resolution
30//! 2. **Priority-Based Ordering**: Modules with higher priority execute first
31//! 3. **Circular Dependency Detection**: Returns error if circular dependencies exist
32//! 4. **Missing Dependency Detection**: Returns error if a dependency is not found
33//!
34//! ## Usage Example
35//!
36//! ```rust
37//! use dmsc::core::module_sorter::sort_modules;
38//!
39//! let modules = vec![...]; // Module slots
40//! let sorted = sort_modules(modules)?;
41//! ```
42
43use crate::core::DMSCResult;
44use std::collections::HashMap;
45use super::module_types::ModuleSlot;
46
47/// Sort modules based on dependencies and priority
48/// Uses topological sort to handle dependencies, and sorts by priority within the same dependency level
49pub(crate) fn sort_modules(modules: Vec<ModuleSlot>) -> DMSCResult<Vec<ModuleSlot>> {
50 let mut modules = modules;
51 let mut result: Vec<ModuleSlot> = Vec::with_capacity(modules.len());
52
53 // Loop until all modules are processed
54 while !modules.is_empty() {
55 // Create a map from module name to current index
56 let name_to_index: HashMap<&str, usize> = modules
57 .iter()
58 .enumerate()
59 .map(|(i, slot)| (slot.module.name(), i))
60 .collect();
61
62 // Calculate in-degree for each module
63 let mut in_degree: Vec<usize> = vec![0; modules.len()];
64
65 for (i, slot) in modules.iter().enumerate() {
66 let dependencies = slot.module.dependencies();
67 for dep_name in dependencies {
68 // Check if dependency exists in remaining modules
69 if let Some(_dep_index) = name_to_index.get(dep_name) {
70 // Dependency exists, so this module has in-degree
71 in_degree[i] += 1;
72 } else {
73 // Dependency not found, check if it's already in result
74 let dep_in_result = result.iter().any(|slot| slot.module.name() == dep_name);
75 if !dep_in_result {
76 return Err(crate::core::DMSCError::MissingDependency {
77 module_name: slot.module.name().to_string(),
78 dependency: dep_name.to_string()
79 });
80 }
81 }
82 }
83 }
84
85 // Collect all modules with in-degree 0
86 let mut zero_in_degree: Vec<(i32, usize)> = in_degree
87 .iter()
88 .enumerate()
89 .filter(|&(_, °ree)| degree == 0)
90 .map(|(i, _)| (modules[i].module.priority(), i))
91 .collect();
92
93 // If no modules with in-degree 0, we have a circular dependency
94 if zero_in_degree.is_empty() {
95 return Err(crate::core::DMSCError::CircularDependency {
96 modules: modules.iter().map(|slot| slot.module.name().to_string()).collect()
97 });
98 }
99
100 // Sort modules with in-degree 0 by priority (descending), then by index (ascending)
101 zero_in_degree.sort_by(|a, b| {
102 // Higher priority comes first, and if priorities are equal, lower index comes first
103 a.0.cmp(&b.0).reverse().then_with(|| a.1.cmp(&b.1))
104 });
105
106 // Process modules with in-degree 0 in sorted order
107 // We need to collect indices in a list and process them in reverse order
108 // to avoid index shifting issues when removing elements
109 let mut indices_to_remove: Vec<usize> = zero_in_degree
110 .iter()
111 .map(|&(_, i)| i)
112 .collect();
113
114 // Sort indices in descending order to avoid index shifting
115 indices_to_remove.sort_by(|a, b| b.cmp(a));
116
117 // Add modules to result in the correct order
118 let mut added_modules: Vec<ModuleSlot> = Vec::with_capacity(indices_to_remove.len());
119 for &i in &indices_to_remove {
120 let module = modules.swap_remove(i);
121 added_modules.push(module);
122 }
123
124 // Reverse the added modules to get the original sorted order
125 added_modules.reverse();
126 result.extend(added_modules);
127 }
128
129 Ok(result)
130}