check_deprule/dependency_graph/
mod.rs1use anyhow::{Error, anyhow};
2use cargo_metadata::{DependencyKind, Metadata, Package, PackageId};
3use petgraph::graph::NodeIndex;
4use petgraph::stable_graph::StableGraph;
5use petgraph::visit::Dfs;
6use std::collections::HashMap;
7
8pub(crate) mod formatter;
9pub mod tree;
10
11#[derive(Debug, Clone)]
12pub struct Graph {
13 pub graph: StableGraph<Package, DependencyKind>,
14 pub nodes: HashMap<PackageId, NodeIndex>,
15 pub root: Option<PackageId>,
16}
17
18#[derive(Debug, Clone, Default)]
19pub struct DependencyGraphBuildConfigs {
20 no_dev_dependencies: bool,
21}
22impl DependencyGraphBuildConfigs {
23 pub fn new(no_dev_dependencies: bool) -> Self {
24 Self {
25 no_dev_dependencies,
26 }
27 }
28}
29
30pub fn build_dependency_graph(
31 metadata: Metadata,
32 config: DependencyGraphBuildConfigs,
33) -> Result<Graph, Error> {
34 let resolve = metadata.resolve.unwrap();
35
36 let mut graph = Graph {
37 graph: StableGraph::new(),
38 nodes: HashMap::new(),
39 root: resolve.root,
40 };
41
42 for package in metadata.packages {
43 let id = package.id.clone();
44 let index = graph.graph.add_node(package);
45 graph.nodes.insert(id, index);
46 }
47
48 for node in resolve.nodes {
49 if node.deps.len() != node.dependencies.len() {
50 return Err(anyhow!("cargo tree requires cargo 1.41 or newer"));
51 }
52
53 let from = graph.nodes[&node.id];
54 for dep in node.deps {
55 if dep.dep_kinds.is_empty() {
56 return Err(anyhow!("cargo tree requires cargo 1.41 or newer"));
57 }
58
59 let mut kinds = vec![];
61 for kind in dep.dep_kinds {
62 if !kinds.iter().any(|k| *k == kind.kind) {
63 kinds.push(kind.kind);
64 }
65 }
66
67 let to = graph.nodes[&dep.pkg];
68 for kind in kinds {
69 if config.no_dev_dependencies && kind == DependencyKind::Development {
70 continue;
71 }
72
73 graph.graph.add_edge(from, to, kind);
74 }
75 }
76 }
77
78 if let Some(root) = &graph.root {
80 let mut dfs = Dfs::new(&graph.graph, graph.nodes[root]);
81 while dfs.next(&graph.graph).is_some() {}
82
83 let g = &mut graph.graph;
84 graph.nodes.retain(|_, idx| {
85 if !dfs.discovered.contains(idx.index()) {
86 g.remove_node(*idx);
87 false
88 } else {
89 true
90 }
91 });
92 }
93
94 Ok(graph)
95}