gh_stack/
graph.rs

1use petgraph::visit::Bfs;
2use petgraph::visit::EdgeRef;
3use petgraph::{Direction, Graph};
4use std::collections::HashMap;
5use std::rc::Rc;
6
7use crate::api::search::PullRequest;
8
9pub type FlatDep = Vec<(Rc<PullRequest>, Option<Rc<PullRequest>>)>;
10
11pub fn build(prs: &[Rc<PullRequest>]) -> Graph<Rc<PullRequest>, usize> {
12    let mut tree = Graph::<Rc<PullRequest>, usize>::new();
13    let heads = prs.iter().map(|pr| pr.head());
14    let handles: Vec<_> = prs.iter().map(|pr| tree.add_node(pr.clone())).collect();
15    let handles_by_head: HashMap<_, _> = heads.zip(handles.iter()).collect();
16
17    for (i, pr) in prs.iter().enumerate() {
18        let head_handle = handles[i];
19        if let Some(&base_handle) = handles_by_head.get(pr.base()) {
20            tree.add_edge(*base_handle, head_handle, 1);
21        }
22    }
23
24    tree
25}
26
27/// Return a flattened list of graph nodes as tuples; each tuple is `(node, node's parent [if exists])`.
28/// TODO: Panic if this isn't a single flat list of dependencies
29pub fn log(graph: &Graph<Rc<PullRequest>, usize>) -> FlatDep {
30    let roots: Vec<_> = graph.externals(Direction::Incoming).collect();
31    let mut out = Vec::new();
32
33    for root in roots {
34        let mut bfs = Bfs::new(&graph, root);
35        while let Some(node) = bfs.next(&graph) {
36            let parent = graph.edges_directed(node, Direction::Incoming).next();
37            let node: Rc<PullRequest> = graph[node].clone();
38
39            match parent {
40                Some(parent) => out.push((node, Some(graph[parent.source()].clone()))),
41                None => out.push((node, None)),
42            }
43        }
44    }
45
46    out.sort_by_key(|(dep, _)| {
47        dep.state().clone()
48    });
49
50    out
51}