1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
pub use petgraph;
use crate::{
error::{Error, ErrorKind},
lockfile::Lockfile,
package::{self, Package},
};
use petgraph::graph::NodeIndex;
use std::collections::BTreeMap as Map;
pub type Graph = petgraph::graph::Graph<Package, Edge>;
pub type Nodes = Map<package::Release, NodeIndex>;
#[derive(Clone, Debug)]
pub struct DependencyGraph {
graph: Graph,
nodes: Nodes,
root_node: NodeIndex,
}
impl DependencyGraph {
pub fn new(lockfile: &Lockfile) -> Result<Self, Error> {
let mut graph = Graph::new();
let mut nodes = Map::new();
let root_package = find_root_package(lockfile)?;
let root_node = graph.add_node(root_package.clone());
nodes.insert(root_package.release(), root_node);
let mut dep_graph = Self {
graph,
nodes,
root_node,
};
dep_graph.add_dependencies(lockfile, root_package, root_node);
Ok(dep_graph)
}
pub fn graph(&self) -> &Graph {
&self.graph
}
pub fn nodes(&self) -> &Nodes {
&self.nodes
}
pub fn root_package(&self) -> &Package {
&self.graph[self.root_node]
}
fn add_dependencies(&mut self, lockfile: &Lockfile, package: &Package, parent: NodeIndex) {
for dependency in lockfile.dependencies(package) {
let node = self.graph.add_node(dependency.clone());
self.nodes.insert(dependency.release(), node);
self.graph.add_edge(parent, node, Edge);
self.add_dependencies(lockfile, dependency, node);
}
}
}
fn find_root_package(lockfile: &Lockfile) -> Result<&Package, Error> {
let mut dependency_counts = Map::new();
for package in &lockfile.packages {
dependency_counts.entry(&package.name).or_insert(0);
for dependency in &package.dependencies {
*dependency_counts.entry(&dependency.name).or_insert(0) += 1;
}
}
let root_package_name = *dependency_counts
.iter()
.find(|(_, count)| **count == 0)
.ok_or_else(|| format_err!(ErrorKind::Parse, "couldn't find root package"))?
.0;
Ok(lockfile
.packages
.iter()
.find(|package| &package.name == root_package_name)
.unwrap())
}
#[derive(Clone, Debug)]
pub struct Edge;
#[cfg(test)]
mod tests {
use super::*;
fn load_lockfile() -> Lockfile {
Lockfile::load("Cargo.lock").unwrap()
}
#[test]
fn root_package() {
let lockfile = load_lockfile();
assert_eq!(
find_root_package(&lockfile).unwrap().name.as_str(),
"cargo-lock"
);
}
#[test]
fn computed_graph() {
let lockfile = load_lockfile();
let dependencies = DependencyGraph::new(&lockfile).unwrap();
let root_package = dependencies.root_package();
assert_eq!(root_package.name.as_str(), "cargo-lock");
}
}