use std::collections::{HashMap, HashSet};
use std::fmt;
use petgraph::algo::is_cyclic_directed;
use petgraph::graph::DiGraph;
use anyhow::Result;
#[derive(Debug)]
pub struct DependencyManager {
dependency_graph: DiGraph<String, ()>,
node_indices: HashMap<String, petgraph::graph::NodeIndex>,
}
impl Default for DependencyManager {
fn default() -> Self {
Self::new()
}
}
impl DependencyManager {
pub fn new() -> Self {
Self { dependency_graph: DiGraph::new(), node_indices: HashMap::new() }
}
pub fn add_plugin(
&mut self,
plugin_name: &str,
) {
if !self.node_indices.contains_key(plugin_name) {
let idx = self.dependency_graph.add_node(plugin_name.to_string());
self.node_indices.insert(plugin_name.to_string(), idx);
}
}
pub fn add_dependency(
&mut self,
dependent: &str,
dependency: &str,
) -> Result<()> {
self.add_plugin(dependent);
self.add_plugin(dependency);
let dependent_idx = self.node_indices[dependent];
let dependency_idx = self.node_indices[dependency];
self.dependency_graph.add_edge(dependent_idx, dependency_idx, ());
Ok(())
}
pub fn check_missing_dependencies(&self) -> MissingDependencyReport {
let available_plugins: HashSet<String> =
self.node_indices.keys().cloned().collect();
let mut missing_deps = HashMap::new();
let mut total_missing = 0;
for edge in self.dependency_graph.edge_indices() {
let endpoints = match self.dependency_graph.edge_endpoints(edge) {
Some(endpoints) => endpoints,
None => {
tracing::warn!("无法获取边端点,跳过: {:?}", edge);
continue;
},
};
let (source_idx, target_idx) = endpoints;
let dependent = self.dependency_graph[source_idx].clone();
let dependency = self.dependency_graph[target_idx].clone();
if !available_plugins.contains(&dependency) {
missing_deps
.entry(dependent)
.or_insert_with(Vec::new)
.push(dependency.clone());
total_missing += 1;
}
}
MissingDependencyReport {
has_missing_dependencies: !missing_deps.is_empty(),
total_missing_count: total_missing,
missing_dependencies: missing_deps,
available_plugins,
}
}
pub fn has_circular_dependencies(&self) -> bool {
is_cyclic_directed(&self.dependency_graph)
}
pub fn get_circular_dependencies(&self) -> Vec<Vec<String>> {
if !self.has_circular_dependencies() {
return vec![];
}
let mut cycles = Vec::new();
let mut visited = HashSet::new();
let mut rec_stack = HashSet::new();
let mut path = Vec::new();
for node in self.dependency_graph.node_indices() {
if !visited.contains(&node) {
self.dfs_cycles(
node,
&mut visited,
&mut rec_stack,
&mut path,
&mut cycles,
);
}
}
self.deduplicate_cycles(cycles)
}
fn dfs_cycles(
&self,
node: petgraph::graph::NodeIndex,
visited: &mut HashSet<petgraph::graph::NodeIndex>,
rec_stack: &mut HashSet<petgraph::graph::NodeIndex>,
path: &mut Vec<String>,
cycles: &mut Vec<Vec<String>>,
) {
let node_name = self.dependency_graph[node].clone();
visited.insert(node);
rec_stack.insert(node);
path.push(node_name.clone());
for neighbor in self.dependency_graph.neighbors(node) {
if !visited.contains(&neighbor) {
self.dfs_cycles(neighbor, visited, rec_stack, path, cycles);
} else if rec_stack.contains(&neighbor) {
let neighbor_name = self.dependency_graph[neighbor].clone();
if let Some(start_idx) =
path.iter().position(|x| x == &neighbor_name)
{
let cycle = path[start_idx..].to_vec();
if cycle.len() > 1 {
cycles.push(cycle);
}
}
}
}
rec_stack.remove(&node);
path.pop();
}
fn deduplicate_cycles(
&self,
mut cycles: Vec<Vec<String>>,
) -> Vec<Vec<String>> {
if cycles.is_empty() {
return cycles;
}
for cycle in &mut cycles {
self.normalize_cycle(cycle);
}
cycles.sort();
cycles.dedup();
cycles
}
fn normalize_cycle(
&self,
cycle: &mut [String],
) {
if cycle.len() <= 1 {
return;
}
let min_pos = cycle
.iter()
.enumerate()
.min_by_key(|(_, item)| *item)
.map(|(pos, _)| pos)
.unwrap_or(0);
if min_pos > 0 {
cycle.rotate_left(min_pos);
}
}
pub fn get_topological_order(&self) -> Result<Vec<String>> {
if self.has_circular_dependencies() {
return Err(anyhow::anyhow!("存在循环依赖,无法进行拓扑排序"));
}
let order = petgraph::algo::toposort(&self.dependency_graph, None)
.map_err(|_| anyhow::anyhow!("拓扑排序失败"))?;
let mut result = Vec::new();
for node_idx in order {
result.push(self.dependency_graph[node_idx].clone());
}
Ok(result)
}
pub fn get_direct_dependencies(
&self,
plugin_name: &str,
) -> Vec<String> {
if let Some(&node_idx) = self.node_indices.get(plugin_name) {
self.dependency_graph
.neighbors(node_idx)
.map(|idx| self.dependency_graph[idx].clone())
.collect()
} else {
vec![]
}
}
pub fn get_all_dependencies(
&self,
plugin_name: &str,
) -> HashSet<String> {
let mut all_deps = HashSet::new();
let mut to_visit = std::collections::VecDeque::new();
to_visit.push_back(plugin_name.to_string());
while let Some(current) = to_visit.pop_front() {
if all_deps.insert(current.clone()) {
let deps = self.get_direct_dependencies(¤t);
to_visit.extend(deps);
}
}
all_deps.remove(plugin_name);
all_deps
}
pub fn get_circular_dependency_report(&self) -> CircularDependencyReport {
let cycles = self.get_circular_dependencies();
CircularDependencyReport {
has_circular_dependencies: !cycles.is_empty(),
cycle_count: cycles.len(),
cycles: cycles.clone(),
affected_plugins: self.get_affected_plugins(cycles),
}
}
fn get_affected_plugins(
&self,
cycles: Vec<Vec<String>>,
) -> HashSet<String> {
let mut affected = HashSet::new();
for cycle in cycles {
for plugin in cycle {
affected.insert(plugin.clone());
}
}
affected
}
}
#[derive(Debug, Clone)]
pub struct MissingDependencyReport {
pub has_missing_dependencies: bool,
pub total_missing_count: usize,
pub missing_dependencies: HashMap<String, Vec<String>>,
pub available_plugins: HashSet<String>,
}
impl fmt::Display for MissingDependencyReport {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
if !self.has_missing_dependencies {
return write!(f, "✅ 所有依赖都已满足");
}
writeln!(f, "❌ 检测到 {} 个缺失的依赖", self.total_missing_count)?;
writeln!(f, "可用插件: {:?}", self.available_plugins)?;
writeln!(f, "\n缺失依赖详情:")?;
for (plugin_name, missing_deps) in &self.missing_dependencies {
writeln!(f, " {plugin_name} 缺失依赖: {missing_deps:?}")?;
}
Ok(())
}
}
impl MissingDependencyReport {
pub fn get_all_missing_dependency_names(&self) -> HashSet<String> {
let mut all_missing = HashSet::new();
for missing_deps in self.missing_dependencies.values() {
all_missing.extend(missing_deps.iter().cloned());
}
all_missing
}
}
#[derive(Debug, Clone)]
pub struct CircularDependencyReport {
pub has_circular_dependencies: bool,
pub cycle_count: usize,
pub cycles: Vec<Vec<String>>,
pub affected_plugins: HashSet<String>,
}
impl fmt::Display for CircularDependencyReport {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
if !self.has_circular_dependencies {
return write!(f, "✅ 未检测到循环依赖");
}
writeln!(f, "❌ 检测到 {} 个循环依赖", self.cycle_count)?;
writeln!(f, "受影响的插件: {:?}", self.affected_plugins)?;
writeln!(f, "\n循环依赖详情:")?;
for (i, cycle) in self.cycles.iter().enumerate() {
write!(f, " 循环 {}: ", i + 1)?;
for (j, plugin) in cycle.iter().enumerate() {
if j > 0 {
write!(f, " -> ")?;
}
write!(f, "{plugin}")?;
}
if !cycle.is_empty() {
write!(f, " -> {}", cycle[0])?;
}
writeln!(f)?;
}
Ok(())
}
}
impl CircularDependencyReport {}