1use petgraph::{stable_graph::StableDiGraph, Direction};
2
3pub trait Node {
6 type DependencyType;
9
10 fn dependencies(&self) -> &[Self::DependencyType];
12
13 fn matches(&self, dependency: &Self::DependencyType) -> bool;
15}
16
17pub enum Step<'a, N: Node> {
24 Resolved(&'a N),
25 Unresolved(&'a N::DependencyType),
26}
27
28impl<'a, N: Node> Step<'a, N> {
29 pub fn is_resolved(&self) -> bool {
30 match self {
31 Step::Resolved(_) => true,
32 Step::Unresolved(_) => false,
33 }
34 }
35
36 pub fn as_resolved(&self) -> Option<&N> {
37 match self {
38 Step::Resolved(node) => Some(node),
39 Step::Unresolved(_) => None,
40 }
41 }
42
43 pub fn as_unresolved(&self) -> Option<&N::DependencyType> {
44 match self {
45 Step::Resolved(_) => None,
46 Step::Unresolved(dependency) => Some(dependency),
47 }
48 }
49}
50
51pub struct DependencyGraph<'a, N: Node> {
54 graph: StableDiGraph<Step<'a, N>, &'a N::DependencyType>,
55}
56
57impl<'a, N> From<&'a [N]> for DependencyGraph<'a, N>
61where
62 N: Node,
63{
64 fn from(nodes: &'a [N]) -> Self {
65 let mut graph = StableDiGraph::<Step<'a, N>, &'a N::DependencyType>::new();
66
67 let nodes: Vec<(_, _)> = nodes
71 .iter()
72 .map(|node| (node, graph.add_node(Step::Resolved(node))))
73 .collect();
74
75 for (node, index) in nodes.iter() {
76 for dependency in node.dependencies() {
77 if let Some((_, dependent)) = nodes.iter().find(|(dep, _)| dep.matches(dependency))
79 {
80 graph.add_edge(*index, *dependent, dependency);
82 } else {
83 let unresolved = graph.add_node(Step::Unresolved(dependency));
85 graph.add_edge(*index, unresolved, dependency);
86 }
87 }
88 }
89
90 Self { graph }
91 }
92}
93
94impl<'a, N> DependencyGraph<'a, N>
95where
96 N: Node,
97{
98 pub fn is_internally_resolvable(&self) -> bool {
101 self.graph.node_weights().all(Step::is_resolved)
102 }
103
104 pub fn unresolved_dependencies(&self) -> impl Iterator<Item = &N::DependencyType> {
108 self.graph.node_weights().filter_map(Step::as_unresolved)
109 }
110}
111
112impl<'a, N> Iterator for DependencyGraph<'a, N>
116where
117 N: Node,
118{
119 type Item = Step<'a, N>;
120
121 fn next(&mut self) -> Option<Self::Item> {
122 for index in self.graph.node_indices().rev() {
125 if self
126 .graph
127 .neighbors_directed(index, Direction::Outgoing)
128 .count()
129 == 0
130 {
131 return self.graph.remove_node(index);
132 }
133 }
134
135 None
136 }
137}
138
139#[cfg(test)]
140mod tests {
141
142 use crate::{DependencyGraph, Node, Step};
143 use semver::{BuildMetadata, Prerelease, Version, VersionReq};
144
145 #[derive(Debug)]
146 struct Package {
147 name: &'static str,
148 version: Version,
149 dependencies: Vec<Dependency>,
150 }
151
152 #[derive(Debug)]
153 struct Dependency {
154 name: &'static str,
155 version: VersionReq,
156 }
157
158 impl Node for Package {
159 type DependencyType = Dependency;
160
161 fn dependencies(&self) -> &[Self::DependencyType] {
162 &self.dependencies[..]
163 }
164
165 fn matches(&self, dependency: &Self::DependencyType) -> bool {
166 self.name == dependency.name && dependency.version.matches(&self.version)
167 }
168 }
169
170 #[test]
171 fn test_dependencies_synchronous() {
172 let build = build_test_graph();
173 let graph = DependencyGraph::from(&build[..]);
174
175 assert!(!graph.is_internally_resolvable());
176
177 for node in graph {
178 match node {
179 Step::Resolved(build) => println!("build: {:?}", build.name),
180 Step::Unresolved(lookup) => println!("lookup: {:?}", lookup.name),
181 }
182 }
183 }
184
185 #[test]
186 fn test_unresolved_dependencies() {
187 let build = build_test_graph();
188 let graph = DependencyGraph::from(&build[..]);
189
190 assert!(!graph.is_internally_resolvable());
191
192 let unresolved_dependencies: Vec<_> = graph
193 .unresolved_dependencies()
194 .map(|dep| dep.name)
195 .collect();
196
197 assert_eq!(unresolved_dependencies, vec!["unknown", "remote"]);
198 }
199
200 #[test]
201 fn test_generate_dependency_graph() {
202 DependencyGraph::from(&build_test_graph()[..]);
203 }
204
205 fn build_test_graph() -> Vec<Package> {
206 vec![
207 Package {
208 name: "base",
209 version: semver::Version {
210 major: 1,
211 minor: 2,
212 patch: 3,
213 pre: Prerelease::new("").unwrap(),
214 build: BuildMetadata::EMPTY,
215 },
216 dependencies: vec![],
217 },
218 Package {
219 name: "derived",
220 version: semver::Version {
221 major: 1,
222 minor: 2,
223 patch: 3,
224 pre: Prerelease::new("").unwrap(),
225 build: BuildMetadata::EMPTY,
226 },
227 dependencies: vec![Dependency {
228 name: "base",
229 version: ">=1.0.0".parse().unwrap(),
230 }],
231 },
232 Package {
233 name: "second_order",
234 version: semver::Version {
235 major: 1,
236 minor: 2,
237 patch: 3,
238 pre: Prerelease::new("").unwrap(),
239 build: BuildMetadata::EMPTY,
240 },
241 dependencies: vec![Dependency {
242 name: "derived",
243 version: ">=1.0.0".parse().unwrap(),
244 }],
245 },
246 Package {
247 name: "converged",
248 version: semver::Version {
249 major: 1,
250 minor: 2,
251 patch: 3,
252 pre: Prerelease::new("").unwrap(),
253 build: BuildMetadata::EMPTY,
254 },
255 dependencies: vec![
256 Dependency {
257 name: "base",
258 version: ">=1.0.0".parse().unwrap(),
259 },
260 Dependency {
261 name: "derived",
262 version: ">=1.0.0".parse().unwrap(),
263 },
264 ],
265 },
266 Package {
267 name: "independent",
268 version: semver::Version {
269 major: 1,
270 minor: 2,
271 patch: 3,
272 pre: Prerelease::new("").unwrap(),
273 build: BuildMetadata::EMPTY,
274 },
275 dependencies: vec![],
276 },
277 Package {
278 name: "external",
279 version: semver::Version {
280 major: 1,
281 minor: 2,
282 patch: 3,
283 pre: Prerelease::new("").unwrap(),
284 build: BuildMetadata::EMPTY,
285 },
286 dependencies: vec![
287 Dependency {
288 name: "unknown",
289 version: ">=1.0.0".parse().unwrap(),
290 },
291 Dependency {
292 name: "remote",
293 version: "=3.0.0".parse().unwrap(),
294 },
295 ],
296 },
297 ]
298 }
299
300 #[test]
301 fn test_internally_resolved() {
302 let packages = vec![
303 Package {
304 name: "base",
305 version: semver::Version {
306 major: 1,
307 minor: 2,
308 patch: 3,
309 pre: Prerelease::new("").unwrap(),
310 build: BuildMetadata::EMPTY,
311 },
312 dependencies: vec![],
313 },
314 Package {
315 name: "derived",
316 version: semver::Version {
317 major: 3,
318 minor: 2,
319 patch: 0,
320 pre: Prerelease::new("").unwrap(),
321 build: BuildMetadata::EMPTY,
322 },
323 dependencies: vec![Dependency {
324 name: "base",
325 version: "=1.2.3".parse().unwrap(),
326 }],
327 },
328 Package {
329 name: "second_order",
330 version: semver::Version {
331 major: 11,
332 minor: 2,
333 patch: 4,
334 pre: Prerelease::new("").unwrap(),
335 build: BuildMetadata::EMPTY,
336 },
337 dependencies: vec![Dependency {
338 name: "derived",
339 version: ">=3.0.0".parse().unwrap(),
340 }],
341 },
342 ];
343
344 let graph = DependencyGraph::from(&packages[..]);
345
346 for package in graph {
347 match package {
348 Step::Resolved(package) => println!("Building {}!", package.name),
350
351 Step::Unresolved(_) => unreachable!(),
358 }
359 }
360 }
361}