diff --git a/src/graph.rs b/src/graph.rs index 99c8190..bac5bbc 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,6 +1,8 @@ -use petgraph::Graph; -use std::collections::HashMap; +use petgraph::visit::Bfs; +use petgraph::visit::EdgeRef; +use petgraph::{Direction, Graph}; use std::rc::Rc; +use std::collections::HashMap; use crate::api::search::PullRequest; @@ -19,3 +21,24 @@ pub fn build(prs: &Vec>) -> Graph, usize> { tree } + +/// Return a flattened list of graph nodes as tuples; each tuple is `(node, node's parent [if exists])`. +pub fn log(graph: &Graph, usize>) -> Vec<(Rc, Option>)> { + let roots: Vec<_> = graph.externals(Direction::Incoming).collect(); + let mut out = Vec::new(); + + for root in roots { + let mut bfs = Bfs::new(&graph, root); + while let Some(node) = bfs.next(&graph) { + let parent = graph.edges_directed(node, Direction::Incoming).next(); + let node: Rc = graph[node].clone(); + + match parent { + Some(parent) => out.push((node, Some(graph[parent.source()].clone()))), + None => out.push((node, None)), + } + } + } + + out +} diff --git a/src/main.rs b/src/main.rs index edae68f..989b67b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,13 +35,14 @@ async fn main() -> Result<(), Box> { let env: HashMap = env::vars().collect(); let args: Vec = env::args().collect(); - if args.len() > 3 { - println!("usage: gh-stack "); + if args.len() > 4 { + println!("usage: gh-stack "); process::exit(1); } - let pattern = &args[1]; - let prelude = args.get(2); + let command = &args[1][..]; + let pattern = &args[2]; + let prelude = args.get(3); let token = env .get("GHSTACK_OAUTH_TOKEN") @@ -52,22 +53,40 @@ async fn main() -> Result<(), Box> { let prs = api::search::fetch_pull_requests_matching(&pattern, &credentials).await?; let prs = prs.into_iter().map(|pr| Rc::new(pr)).collect(); let tree = graph::build(&prs); - let table = markdown::build_table(tree, pattern); - let output = match prelude { - Some(prelude) => build_final_output(prelude, &table), - None => table + match command { + "github" => { + let table = markdown::build_table(tree, pattern); + + let output = match prelude { + Some(prelude) => build_final_output(prelude, &table), + None => table + }; + + for pr in prs.iter() { + println!("{}: {}", pr.number(), pr.title()); + } + + let response = read_cli_input("Going to update these PRs ☝️ (y/n): "); + match &response[..] { + "y" => persist::persist(&prs, &output, &credentials).await?, + _ => std::process::exit(1), + } + } + + "log" => { + let log = graph::log(&tree); + for (pr, maybe_parent) in log { + match maybe_parent { + Some(parent) => println!("{} → {}", pr.head(), parent.head()), + None => println!("{} → N/A", pr.head()) + } + } + } + + _ => { panic!("Invalid command!") } }; - for pr in prs.iter() { - println!("{}: {}", pr.number(), pr.title()); - } - - let response = read_cli_input("Going to update these PRs ☝️ (y/n): "); - match &response[..] { - "y" => persist::persist(&prs, &output, &credentials).await?, - _ => std::process::exit(1), - } println!("Done!"); @@ -80,6 +99,7 @@ async fn main() -> Result<(), Box> { - [x] Create markdown table - [x] Persist table back to Github - [x] Accept a prelude via STDIN + - [x] Log a textual representation of the graph - [ ] Panic on non-200s */ } diff --git a/src/markdown.rs b/src/markdown.rs index 15858e9..1dc6fe4 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -18,6 +18,7 @@ pub fn build_table(graph: Graph, usize>, title: &str) -> String out.push_str("| PR | Title | Merges Into |\n"); out.push_str("|:--:|:------|:-------------:|\n"); + // TODO: Use graph::log to simplify this let roots: Vec<_> = graph.externals(Direction::Incoming).collect(); for root in roots {