dependency_graph/
lib.rs

1use petgraph::{stable_graph::StableDiGraph, Direction};
2
3/// Must be implemented by the type you wish
4/// to build a dependency graph for. See the README.md for an example
5pub trait Node {
6    /// Encodes a dependency relationship. In a Package Manager dependency graph for intance, this might be a (package name, version) tuple.
7    /// It might also just be the exact same type as the one that implements the Node trait, in which case `Node::matches` can be implemented through simple equality.
8    type DependencyType;
9
10    /// Returns a slice of dependencies for this Node
11    fn dependencies(&self) -> &[Self::DependencyType];
12
13    /// Returns true if the `dependency` can be met by us.
14    fn matches(&self, dependency: &Self::DependencyType) -> bool;
15}
16
17/// Wrapper around dependency graph nodes.
18/// Since a graph might have dependencies that cannot be resolved internally,
19/// this wrapper is necessary to differentiate between internally resolved and
20/// externally (unresolved) dependencies.
21/// An Unresolved dependency does not necessarily mean that it *cannot* be resolved,
22/// only that no Node within the graph fulfills it.
23pub 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
51/// The [`DependencyGraph`] structure builds an internal [Directed Graph](`petgraph::stable_graph::StableDiGraph`), which can then be traversed
52/// in an order which ensures that dependent Nodes are visited before their parents.
53pub struct DependencyGraph<'a, N: Node> {
54    graph: StableDiGraph<Step<'a, N>, &'a N::DependencyType>,
55}
56
57/// The only way to build a [`DependencyGraph`] is from a slice of objects implementing [`Node`].
58/// The graph references the original items, meaning the objects cannot be modified while
59/// the [`DependencyGraph`] holds a reference to them.
60impl<'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        // Insert the input nodes into the graph, and record their positions.
68        // We'll be adding the edges next, and filling in any unresolved
69        // steps we find along the way.
70        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                // Check to see if we can resolve this dependency internally.
78                if let Some((_, dependent)) = nodes.iter().find(|(dep, _)| dep.matches(dependency))
79                {
80                    // If we can, just add an edge between the two nodes.
81                    graph.add_edge(*index, *dependent, dependency);
82                } else {
83                    // If not, create a new "Unresolved" node, and create an edge to that.
84                    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    /// True if all graph [`Node`]s have only references to other internal [`Node`]s.
99    /// That is, there are no unresolved dependencies between nodes.
100    pub fn is_internally_resolvable(&self) -> bool {
101        self.graph.node_weights().all(Step::is_resolved)
102    }
103
104    /// Get an iterator over unresolved dependencies, without traversing the whole graph.
105    /// Useful for doing pre-validation or pre-fetching of external dependencies before
106    /// starting to resolve internal dependencies.
107    pub fn unresolved_dependencies(&self) -> impl Iterator<Item = &N::DependencyType> {
108        self.graph.node_weights().filter_map(Step::as_unresolved)
109    }
110}
111
112/// Iterate over the DependencyGraph in an order which ensures dependencies are resolved before each Node is visited.
113/// Note: If a `Step::Unresolved` node is returned, it is the caller's responsibility to ensure the dependency is resolved
114/// before continuing.
115impl<'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        // Returns the first node, which does not have any Outgoing
123        // edges, which means it is terminal.
124        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                // Print out the package name so we can verify the order ourselves
349                Step::Resolved(package) => println!("Building {}!", package.name),
350
351                // Since we know that all our Packages only have internal references to each other,
352                // we can safely ignore any Unresolved steps in the graph.
353                //
354                // If for example `second_order` required some unknown package `external_package`,
355                // iterating over our graph would yield that as a Step::Unresolved *before*
356                // the `second_order` package.
357                Step::Unresolved(_) => unreachable!(),
358            }
359        }
360    }
361}