f2 feat(log): add repository argument (#4)
* fix(search): remove body param * feat(search): add repo filter * feat(log): add repository argument * chore: gitignore vscode * chore: delete .vscode * feat(annotate): add repository argument
This commit is contained in:
parent
d6aa8192ed
commit
8fdba69645
|
@ -8,3 +8,4 @@ Cargo.lock
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
.vscode
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"workbench.colorTheme": "Ayu Mirage Bordered"
|
|
||||||
}
|
|
|
@ -44,7 +44,47 @@ pub async fn fetch_pull_requests_matching(
|
||||||
&credentials,
|
&credentials,
|
||||||
"https://api.github.com/search/issues",
|
"https://api.github.com/search/issues",
|
||||||
)
|
)
|
||||||
.query(&[("q", format!("\"{}\" in:title,body", pattern))]);
|
.query(&[("q", format!("{} in:title", pattern))]);
|
||||||
|
|
||||||
|
let items = request.send().await?.json::<SearchResponse>().await?.items;
|
||||||
|
|
||||||
|
let item_futures = items.into_iter().map(|item| {
|
||||||
|
api::base_request(&client, &credentials, &item.url.replace("issues", "pulls")).send()
|
||||||
|
});
|
||||||
|
|
||||||
|
// The `unwrap`s are required here because both `reqwest::send` and `reqwest::json` return a `Result` which has
|
||||||
|
// to be unwrapped after the future has been `await`ed on.
|
||||||
|
let items = join_all(item_futures)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| item.unwrap());
|
||||||
|
|
||||||
|
let responses: Vec<_> = join_all(items.map(|item| item.json::<PullRequest>()))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| async {
|
||||||
|
let pr = item.unwrap();
|
||||||
|
let pr = pr.fetch_reviews(credentials).await.unwrap();
|
||||||
|
pr
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(join_all(responses).await)
|
||||||
|
}
|
||||||
|
pub async fn fetch_matching_pull_requests_from_repository(
|
||||||
|
pattern: &str,
|
||||||
|
repository: &str,
|
||||||
|
credentials: &Credentials,
|
||||||
|
) -> Result<Vec<PullRequest>, Box<dyn Error>> {
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let request = api::base_request(
|
||||||
|
&client,
|
||||||
|
&credentials,
|
||||||
|
"https://api.github.com/search/issues",
|
||||||
|
)
|
||||||
|
.query(&[("q", format!("{} in:title repo:{}", pattern, repository))]);
|
||||||
|
|
||||||
let items = request.send().await?.json::<SearchResponse>().await?.items;
|
let items = request.send().await?.json::<SearchResponse>().await?.items;
|
||||||
|
|
||||||
|
|
57
src/main.rs
57
src/main.rs
|
@ -17,6 +17,12 @@ fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("All pull requests containing this identifier in their title form a stack");
|
.help("All pull requests containing this identifier in their title form a stack");
|
||||||
|
|
||||||
|
let repository = Arg::with_name("repository")
|
||||||
|
.long("repository")
|
||||||
|
.short("r")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Remote repository to filter identifier search results by");
|
||||||
|
|
||||||
let exclude = Arg::with_name("exclude")
|
let exclude = Arg::with_name("exclude")
|
||||||
.long("excl")
|
.long("excl")
|
||||||
.short("e")
|
.short("e")
|
||||||
|
@ -29,6 +35,7 @@ fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||||
.setting(AppSettings::ArgRequiredElseHelp)
|
.setting(AppSettings::ArgRequiredElseHelp)
|
||||||
.arg(identifier.clone())
|
.arg(identifier.clone())
|
||||||
.arg(exclude.clone())
|
.arg(exclude.clone())
|
||||||
|
.arg(repository.clone())
|
||||||
.arg(Arg::with_name("prelude")
|
.arg(Arg::with_name("prelude")
|
||||||
.long("prelude")
|
.long("prelude")
|
||||||
.short("p")
|
.short("p")
|
||||||
|
@ -39,7 +46,8 @@ fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||||
.about("Print a list of all pull requests in a stack to STDOUT")
|
.about("Print a list of all pull requests in a stack to STDOUT")
|
||||||
.setting(AppSettings::ArgRequiredElseHelp)
|
.setting(AppSettings::ArgRequiredElseHelp)
|
||||||
.arg(exclude.clone())
|
.arg(exclude.clone())
|
||||||
.arg(identifier.clone());
|
.arg(identifier.clone())
|
||||||
|
.arg(repository.clone());
|
||||||
|
|
||||||
let autorebase = SubCommand::with_name("autorebase")
|
let autorebase = SubCommand::with_name("autorebase")
|
||||||
.about("Rebuild a stack based on changes to local branches and mirror these changes up to the remote")
|
.about("Rebuild a stack based on changes to local branches and mirror these changes up to the remote")
|
||||||
|
@ -100,6 +108,24 @@ async fn build_pr_stack(
|
||||||
Ok(stack)
|
Ok(stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn build_pr_stack_for_repo(
|
||||||
|
pattern: &str,
|
||||||
|
repository: &str,
|
||||||
|
credentials: &Credentials,
|
||||||
|
exclude: Vec<String>,
|
||||||
|
) -> Result<FlatDep, Box<dyn Error>> {
|
||||||
|
let prs = api::search::fetch_matching_pull_requests_from_repository(pattern, repository, &credentials).await?;
|
||||||
|
|
||||||
|
let prs = prs
|
||||||
|
.into_iter()
|
||||||
|
.filter(|pr| !exclude.contains(&pr.number().to_string()))
|
||||||
|
.map(Rc::new)
|
||||||
|
.collect::<Vec<Rc<PullRequest>>>();
|
||||||
|
let graph = graph::build(&prs);
|
||||||
|
let stack = graph::log(&graph);
|
||||||
|
Ok(stack)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_excluded(m: &ArgMatches) -> Vec<String> {
|
fn get_excluded(m: &ArgMatches) -> Vec<String> {
|
||||||
let excluded = m.values_of("exclude");
|
let excluded = m.values_of("exclude");
|
||||||
|
|
||||||
|
@ -120,7 +146,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("annotate", Some(m)) => {
|
("annotate", Some(m)) => {
|
||||||
let identifier = m.value_of("identifier").unwrap();
|
let identifier = m.value_of("identifier").unwrap();
|
||||||
let stack = build_pr_stack(identifier, &credentials, get_excluded(m)).await?;
|
// If no repository is specified, use build_pr_stack. Otherwise, use
|
||||||
|
// build_pr_stack_for_repo.
|
||||||
|
let stack = if m.value_of("repository").is_none() {
|
||||||
|
build_pr_stack(identifier, &credentials, get_excluded(m)).await?
|
||||||
|
} else {
|
||||||
|
let repository = m.value_of("repository").unwrap();
|
||||||
|
println!(
|
||||||
|
"Searching for {} identifier in {} repo",
|
||||||
|
style(identifier).bold(),
|
||||||
|
style(repository).bold()
|
||||||
|
);
|
||||||
|
build_pr_stack_for_repo(identifier, repository, &credentials, get_excluded(m)).await?
|
||||||
|
};
|
||||||
let table = markdown::build_table(&stack, identifier, m.value_of("prelude"));
|
let table = markdown::build_table(&stack, identifier, m.value_of("prelude"));
|
||||||
|
|
||||||
for (pr, _) in stack.iter() {
|
for (pr, _) in stack.iter() {
|
||||||
|
@ -135,7 +173,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
("log", Some(m)) => {
|
("log", Some(m)) => {
|
||||||
let identifier = m.value_of("identifier").unwrap();
|
let identifier = m.value_of("identifier").unwrap();
|
||||||
let stack = build_pr_stack(identifier, &credentials, get_excluded(m)).await?;
|
|
||||||
|
// If no repository is specified, use build_pr_stack. Otherwise, use
|
||||||
|
// build_pr_stack_for_repo.
|
||||||
|
let stack = if m.value_of("repository").is_none() {
|
||||||
|
build_pr_stack(identifier, &credentials, get_excluded(m)).await?
|
||||||
|
} else {
|
||||||
|
let repository = m.value_of("repository").unwrap();
|
||||||
|
println!(
|
||||||
|
"Searching for {} identifier in {} repo",
|
||||||
|
style(identifier).bold(),
|
||||||
|
style(repository).bold()
|
||||||
|
);
|
||||||
|
build_pr_stack_for_repo(identifier, repository, &credentials, get_excluded(m)).await?
|
||||||
|
};
|
||||||
|
|
||||||
for (pr, maybe_parent) in stack {
|
for (pr, maybe_parent) in stack {
|
||||||
match maybe_parent {
|
match maybe_parent {
|
||||||
|
|
Loading…
Reference in New Issue