1use std::any::TypeId;
9use std::collections::{HashMap, VecDeque};
10
11pub struct ModuleEntry {
13 pub type_id: TypeId,
15 pub name: &'static str,
17 pub dependencies: Vec<(&'static str, TypeId)>,
19}
20
21pub struct DependencyGraph {
23 entries: Vec<ModuleEntry>,
24 index: HashMap<TypeId, usize>,
25}
26
27impl DependencyGraph {
28 #[must_use]
30 pub fn new() -> Self {
31 DependencyGraph {
32 entries: Vec::new(),
33 index: HashMap::new(),
34 }
35 }
36
37 pub fn add(&mut self, entry: ModuleEntry) -> Result<(), &'static str> {
43 if self.index.contains_key(&entry.type_id) {
44 return Err(entry.name);
45 }
46 let idx = self.entries.len();
47 self.index.insert(entry.type_id, idx);
48 self.entries.push(entry);
49 Ok(())
50 }
51
52 pub fn validate(&self) -> Result<Vec<TypeId>, GraphError> {
60 for entry in &self.entries {
62 for (dep_name, dep_id) in &entry.dependencies {
63 if !self.index.contains_key(dep_id) {
64 return Err(GraphError::DependencyMissing {
65 module: entry.name,
66 missing: dep_name,
67 });
68 }
69 }
70 }
71
72 let n = self.entries.len();
74 let mut in_degree = vec![0usize; n];
75 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
76
77 for (i, entry) in self.entries.iter().enumerate() {
78 for (_dep_name, dep_id) in &entry.dependencies {
79 if let Some(&dep_idx) = self.index.get(dep_id) {
80 adj[dep_idx].push(i);
81 in_degree[i] += 1;
82 }
83 }
84 }
85
86 let mut queue: VecDeque<usize> = VecDeque::new();
87 for (i, deg) in in_degree.iter().enumerate().take(n) {
88 if *deg == 0 {
89 queue.push_back(i);
90 }
91 }
92
93 let mut sorted = Vec::with_capacity(n);
94 while let Some(node) = queue.pop_front() {
95 sorted.push(self.entries[node].type_id);
96 for &neighbor in &adj[node] {
97 in_degree[neighbor] -= 1;
98 if in_degree[neighbor] == 0 {
99 queue.push_back(neighbor);
100 }
101 }
102 }
103
104 if sorted.len() != n {
105 let cycle = self.find_cycle();
107 return Err(GraphError::CycleDetected { cycle });
108 }
109
110 Ok(sorted)
111 }
112
113 fn find_cycle(&self) -> Vec<&'static str> {
115 fn dfs(
116 node: usize,
117 entries: &[ModuleEntry],
118 index: &HashMap<TypeId, usize>,
119 visited: &mut [u8],
120 stack: &mut Vec<usize>,
121 cycle_names: &mut Vec<&'static str>,
122 ) -> bool {
123 visited[node] = 1;
124 stack.push(node);
125
126 for (_dep_name, dep_id) in &entries[node].dependencies {
127 if let Some(&dep_idx) = index.get(dep_id) {
128 if visited[dep_idx] == 1 {
129 let start = stack
131 .iter()
132 .position(|&x| x == dep_idx)
133 .expect("invariant: dep_idx must be in stack (visited[dep_idx] == 1)");
134 for &idx in &stack[start..] {
135 cycle_names.push(entries[idx].name);
136 }
137 cycle_names.push(entries[dep_idx].name);
138 return true;
139 }
140 if visited[dep_idx] == 0
141 && dfs(dep_idx, entries, index, visited, stack, cycle_names)
142 {
143 return true;
144 }
145 }
146 }
147
148 stack.pop();
149 visited[node] = 2;
150 false
151 }
152
153 let n = self.entries.len();
154 let mut visited = vec![0u8; n]; let mut stack = Vec::new();
156 let mut cycle_names = Vec::new();
157
158 for i in 0..n {
159 if visited[i] == 0
160 && dfs(
161 i,
162 &self.entries,
163 &self.index,
164 &mut visited,
165 &mut stack,
166 &mut cycle_names,
167 )
168 {
169 return cycle_names;
170 }
171 }
172
173 vec!["<unknown cycle>"]
174 }
175
176 #[must_use]
178 pub fn dependency_names(&self, type_id: TypeId) -> Vec<&'static str> {
179 if let Some(&idx) = self.index.get(&type_id) {
180 self.entries[idx]
181 .dependencies
182 .iter()
183 .map(|(name, _)| *name)
184 .collect()
185 } else {
186 Vec::new()
187 }
188 }
189
190 #[must_use]
192 pub fn entries(&self) -> &[ModuleEntry] {
193 &self.entries
194 }
195
196 #[must_use]
198 pub fn name_of(&self, type_id: TypeId) -> Option<&'static str> {
199 self.index.get(&type_id).map(|&idx| self.entries[idx].name)
200 }
201}
202
203impl Default for DependencyGraph {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209#[derive(Debug)]
211pub enum GraphError {
212 DependencyMissing {
214 module: &'static str,
215 missing: &'static str,
216 },
217 CycleDetected { cycle: Vec<&'static str> },
219}