use anyhow::Result;
use cargo_metadata::{Metadata, Package, PackageId};
use petgraph::graph::{DiGraph, NodeIndex};
use std::collections::HashMap;
pub struct DependencyGraph {
graph: DiGraph<PackageId, ()>,
node_map: HashMap<PackageId, NodeIndex>,
metadata: Metadata,
}
impl DependencyGraph {
pub fn from_metadata(metadata: Metadata) -> Result<Self> {
let mut graph = DiGraph::new();
let mut node_map = HashMap::new();
for package in &metadata.packages {
let node = graph.add_node(package.id.clone());
node_map.insert(package.id.clone(), node);
}
for package in &metadata.packages {
let source_node = node_map[&package.id];
for dep in &package.dependencies {
if let Some(resolved) = metadata
.packages
.iter()
.find(|p| p.name == dep.name && dep.req.matches(&p.version))
{
if let Some(&target_node) = node_map.get(&resolved.id) {
graph.add_edge(source_node, target_node, ());
}
}
}
}
Ok(Self {
graph,
node_map,
metadata,
})
}
pub fn find_packages_by_name(&self, name: &str) -> Vec<&Package> {
self.metadata
.packages
.iter()
.filter(|p| p.name == name)
.collect()
}
pub fn find_reverse_dependencies(&self, package_id: &PackageId) -> Vec<&Package> {
let mut result = Vec::new();
if let Some(&target_node) = self.node_map.get(package_id) {
let incoming = self
.graph
.neighbors_directed(target_node, petgraph::Direction::Incoming);
for node in incoming {
let pkg_id = &self.graph[node];
if let Some(pkg) = self.metadata.packages.iter().find(|p| &p.id == pkg_id) {
result.push(pkg);
}
}
}
result
}
pub fn is_workspace_member(&self, package_id: &PackageId) -> bool {
self.metadata
.workspace_members
.iter()
.any(|id| id == package_id)
}
}