1use std::collections::{HashMap, HashSet};
2use std::fmt;
3use petgraph::algo::is_cyclic_directed;
4use petgraph::graph::DiGraph;
5use anyhow::Result;
6
7#[derive(Debug)]
9pub struct DependencyManager {
10 dependency_graph: DiGraph<String, ()>,
11 node_indices: HashMap<String, petgraph::graph::NodeIndex>,
12}
13
14impl Default for DependencyManager {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl DependencyManager {
21 pub fn new() -> Self {
22 Self { dependency_graph: DiGraph::new(), node_indices: HashMap::new() }
23 }
24 pub fn add_plugin(
26 &mut self,
27 plugin_name: &str,
28 ) {
29 if !self.node_indices.contains_key(plugin_name) {
30 let idx = self.dependency_graph.add_node(plugin_name.to_string());
31 self.node_indices.insert(plugin_name.to_string(), idx);
32 }
33 }
34 pub fn add_dependency(
36 &mut self,
37 dependent: &str,
38 dependency: &str,
39 ) -> Result<()> {
40 self.add_plugin(dependent);
42 self.add_plugin(dependency);
43
44 let dependent_idx = self.node_indices[dependent];
46 let dependency_idx = self.node_indices[dependency];
47 self.dependency_graph.add_edge(dependent_idx, dependency_idx, ());
48
49 Ok(())
50 }
51 pub fn check_missing_dependencies(&self) -> MissingDependencyReport {
53 let available_plugins: HashSet<String> =
54 self.node_indices.keys().cloned().collect();
55 let mut missing_deps = HashMap::new();
56 let mut total_missing = 0;
57
58 for edge in self.dependency_graph.edge_indices() {
60 let endpoints = match self.dependency_graph.edge_endpoints(edge) {
62 Some(endpoints) => endpoints,
63 None => {
64 tracing::warn!("无法获取边端点,跳过: {:?}", edge);
65 continue;
66 },
67 };
68
69 let (source_idx, target_idx) = endpoints;
70 let dependent = self.dependency_graph[source_idx].clone();
71 let dependency = self.dependency_graph[target_idx].clone();
72
73 if !available_plugins.contains(&dependency) {
75 missing_deps
76 .entry(dependent)
77 .or_insert_with(Vec::new)
78 .push(dependency.clone());
79 total_missing += 1;
80 }
81 }
82
83 MissingDependencyReport {
84 has_missing_dependencies: !missing_deps.is_empty(),
85 total_missing_count: total_missing,
86 missing_dependencies: missing_deps,
87 available_plugins,
88 }
89 }
90 pub fn has_circular_dependencies(&self) -> bool {
92 is_cyclic_directed(&self.dependency_graph)
93 }
94 pub fn get_circular_dependencies(&self) -> Vec<Vec<String>> {
96 if !self.has_circular_dependencies() {
97 return vec![];
98 }
99
100 let mut cycles = Vec::new();
101 let mut visited = HashSet::new();
102 let mut rec_stack = HashSet::new();
103 let mut path = Vec::new();
104
105 for node in self.dependency_graph.node_indices() {
107 if !visited.contains(&node) {
108 self.dfs_cycles(
109 node,
110 &mut visited,
111 &mut rec_stack,
112 &mut path,
113 &mut cycles,
114 );
115 }
116 }
117
118 self.deduplicate_cycles(cycles)
120 }
121 fn dfs_cycles(
123 &self,
124 node: petgraph::graph::NodeIndex,
125 visited: &mut HashSet<petgraph::graph::NodeIndex>,
126 rec_stack: &mut HashSet<petgraph::graph::NodeIndex>,
127 path: &mut Vec<String>,
128 cycles: &mut Vec<Vec<String>>,
129 ) {
130 let node_name = self.dependency_graph[node].clone();
131 visited.insert(node);
132 rec_stack.insert(node);
133 path.push(node_name.clone());
134
135 for neighbor in self.dependency_graph.neighbors(node) {
137 if !visited.contains(&neighbor) {
138 self.dfs_cycles(neighbor, visited, rec_stack, path, cycles);
140 } else if rec_stack.contains(&neighbor) {
141 let neighbor_name = self.dependency_graph[neighbor].clone();
143
144 if let Some(start_idx) =
146 path.iter().position(|x| x == &neighbor_name)
147 {
148 let cycle = path[start_idx..].to_vec();
150
151 if cycle.len() > 1 {
153 cycles.push(cycle);
154 }
155 }
156 }
157 }
158
159 rec_stack.remove(&node);
161 path.pop();
162 }
163 fn deduplicate_cycles(
165 &self,
166 mut cycles: Vec<Vec<String>>,
167 ) -> Vec<Vec<String>> {
168 if cycles.is_empty() {
169 return cycles;
170 }
171
172 for cycle in &mut cycles {
174 self.normalize_cycle(cycle);
175 }
176
177 cycles.sort();
179 cycles.dedup();
180
181 cycles
182 }
183 fn normalize_cycle(
185 &self,
186 cycle: &mut [String],
187 ) {
188 if cycle.len() <= 1 {
189 return;
190 }
191
192 let min_pos = cycle
194 .iter()
195 .enumerate()
196 .min_by_key(|(_, item)| *item)
197 .map(|(pos, _)| pos)
198 .unwrap_or(0);
199
200 if min_pos > 0 {
202 cycle.rotate_left(min_pos);
203 }
204 }
205
206 pub fn get_topological_order(&self) -> Result<Vec<String>> {
208 if self.has_circular_dependencies() {
209 return Err(anyhow::anyhow!("存在循环依赖,无法进行拓扑排序"));
210 }
211
212 let order = petgraph::algo::toposort(&self.dependency_graph, None)
213 .map_err(|_| anyhow::anyhow!("拓扑排序失败"))?;
214
215 let mut result = Vec::new();
216 for node_idx in order {
217 result.push(self.dependency_graph[node_idx].clone());
218 }
219
220 Ok(result)
221 }
222
223 pub fn get_direct_dependencies(
225 &self,
226 plugin_name: &str,
227 ) -> Vec<String> {
228 if let Some(&node_idx) = self.node_indices.get(plugin_name) {
229 self.dependency_graph
230 .neighbors(node_idx)
231 .map(|idx| self.dependency_graph[idx].clone())
232 .collect()
233 } else {
234 vec![]
235 }
236 }
237 pub fn get_all_dependencies(
239 &self,
240 plugin_name: &str,
241 ) -> HashSet<String> {
242 let mut all_deps = HashSet::new();
243 let mut to_visit = std::collections::VecDeque::new();
244
245 to_visit.push_back(plugin_name.to_string());
246
247 while let Some(current) = to_visit.pop_front() {
248 if all_deps.insert(current.clone()) {
249 let deps = self.get_direct_dependencies(¤t);
250 to_visit.extend(deps);
251 }
252 }
253
254 all_deps.remove(plugin_name);
255 all_deps
256 }
257 pub fn get_circular_dependency_report(&self) -> CircularDependencyReport {
259 let cycles = self.get_circular_dependencies();
260
261 CircularDependencyReport {
262 has_circular_dependencies: !cycles.is_empty(),
263 cycle_count: cycles.len(),
264 cycles: cycles.clone(),
265 affected_plugins: self.get_affected_plugins(cycles),
266 }
267 }
268 fn get_affected_plugins(
270 &self,
271 cycles: Vec<Vec<String>>,
272 ) -> HashSet<String> {
273 let mut affected = HashSet::new();
274
275 for cycle in cycles {
276 for plugin in cycle {
277 affected.insert(plugin.clone());
278 }
279 }
280
281 affected
282 }
283}
284
285#[derive(Debug, Clone)]
287pub struct MissingDependencyReport {
288 pub has_missing_dependencies: bool,
289 pub total_missing_count: usize,
290 pub missing_dependencies: HashMap<String, Vec<String>>,
291 pub available_plugins: HashSet<String>,
292}
293
294impl fmt::Display for MissingDependencyReport {
295 fn fmt(
296 &self,
297 f: &mut fmt::Formatter<'_>,
298 ) -> fmt::Result {
299 if !self.has_missing_dependencies {
300 return write!(f, "✅ 所有依赖都已满足");
301 }
302
303 writeln!(f, "❌ 检测到 {} 个缺失的依赖", self.total_missing_count)?;
304 writeln!(f, "可用插件: {:?}", self.available_plugins)?;
305 writeln!(f, "\n缺失依赖详情:")?;
306
307 for (plugin_name, missing_deps) in &self.missing_dependencies {
308 writeln!(f, " {plugin_name} 缺失依赖: {missing_deps:?}")?;
309 }
310
311 Ok(())
312 }
313}
314
315impl MissingDependencyReport {
316 pub fn get_all_missing_dependency_names(&self) -> HashSet<String> {
318 let mut all_missing = HashSet::new();
319 for missing_deps in self.missing_dependencies.values() {
320 all_missing.extend(missing_deps.iter().cloned());
321 }
322 all_missing
323 }
324}
325
326#[derive(Debug, Clone)]
328pub struct CircularDependencyReport {
329 pub has_circular_dependencies: bool,
330 pub cycle_count: usize,
331 pub cycles: Vec<Vec<String>>,
332 pub affected_plugins: HashSet<String>,
333}
334
335impl fmt::Display for CircularDependencyReport {
336 fn fmt(
337 &self,
338 f: &mut fmt::Formatter<'_>,
339 ) -> fmt::Result {
340 if !self.has_circular_dependencies {
341 return write!(f, "✅ 未检测到循环依赖");
342 }
343
344 writeln!(f, "❌ 检测到 {} 个循环依赖", self.cycle_count)?;
345 writeln!(f, "受影响的插件: {:?}", self.affected_plugins)?;
346 writeln!(f, "\n循环依赖详情:")?;
347
348 for (i, cycle) in self.cycles.iter().enumerate() {
349 write!(f, " 循环 {}: ", i + 1)?;
350
351 for (j, plugin) in cycle.iter().enumerate() {
352 if j > 0 {
353 write!(f, " -> ")?;
354 }
355 write!(f, "{plugin}")?;
356 }
357
358 if !cycle.is_empty() {
360 write!(f, " -> {}", cycle[0])?;
361 }
362
363 writeln!(f)?;
364 }
365
366 Ok(())
367 }
368}
369
370impl CircularDependencyReport {}