hive-forge: surface issues an issue/pr blocks in list/issue/pr
This commit is contained in:
parent
74dfd366b4
commit
a3f14f5126
4 changed files with 88 additions and 18 deletions
|
|
@ -32,7 +32,7 @@ use forgejo_api::structs::{
|
|||
use serde_json::Value;
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::verbs::{dependency_summaries, labels, milestone, print_json};
|
||||
use crate::verbs::{blocking_summaries, dependency_summaries, labels, milestone, print_json};
|
||||
|
||||
/// What kind of items to return. Maps onto Forgejo's `type` query
|
||||
/// parameter: `issues` / `pulls`, or no filter at all for `both`
|
||||
|
|
@ -178,12 +178,18 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
|
|||
return print_json(&items);
|
||||
}
|
||||
for item in items.as_array().into_iter().flatten() {
|
||||
let progress = item
|
||||
.get("number")
|
||||
.and_then(Value::as_u64)
|
||||
let number = item.get("number").and_then(Value::as_u64);
|
||||
let progress = number
|
||||
.map(|n| dependency_summaries(client, owner, name, n))
|
||||
.transpose()?;
|
||||
print_row(item, progress.as_deref().and_then(dep_progress));
|
||||
let blocks = number
|
||||
.map(|n| blocking_summaries(client, owner, name, n))
|
||||
.transpose()?;
|
||||
print_row(
|
||||
item,
|
||||
progress.as_deref().and_then(dep_progress),
|
||||
blocks.as_deref().map_or(0, blocking_open_count),
|
||||
);
|
||||
}
|
||||
if let Some(msg) = trailer(
|
||||
headers.x_total_count.and_then(|t| u64::try_from(t).ok()),
|
||||
|
|
@ -243,11 +249,14 @@ fn trailer(total: Option<u64>, count: u64, page: u64, limit: u64) -> Option<Stri
|
|||
}
|
||||
|
||||
/// Render one issue/PR as a single `#NNN [author] title` line, plus a
|
||||
/// `(N/M deps done)` suffix when `progress` is `Some`.
|
||||
/// Defensive: missing fields drop to placeholders so a partial
|
||||
/// response from a future API change still produces readable output
|
||||
/// instead of panicking on `unwrap`.
|
||||
fn print_row(item: &Value, progress: Option<(usize, usize)>) {
|
||||
/// `(N/M deps done)` suffix when `progress` is `Some` and a `(blocks N)`
|
||||
/// suffix when `blocking_open` is nonzero — the actionability signal the
|
||||
/// operator asked for: an issue blocking open work is worth picking over
|
||||
/// one with no unresolved followers, at a glance in `list`'s own output
|
||||
/// rather than a per-issue `show`. Defensive: missing fields drop to
|
||||
/// placeholders so a partial response from a future API change still
|
||||
/// produces readable output instead of panicking on `unwrap`.
|
||||
fn print_row(item: &Value, progress: Option<(usize, usize)>, blocking_open: usize) {
|
||||
let number = item.get("number").and_then(Value::as_u64).unwrap_or(0);
|
||||
let title = item.get("title").and_then(Value::as_str).unwrap_or("");
|
||||
let author = item
|
||||
|
|
@ -261,12 +270,23 @@ fn print_row(item: &Value, progress: Option<(usize, usize)>) {
|
|||
// (which would render every issue as a PR).
|
||||
let is_pr = item.get("pull_request").is_some_and(|v| !v.is_null());
|
||||
let kind = if is_pr { "PR" } else { " " };
|
||||
match progress {
|
||||
Some((done, total)) => {
|
||||
println!("#{number:>4} {kind} [{author}] {title} ({done}/{total} deps done)");
|
||||
}
|
||||
None => println!("#{number:>4} {kind} [{author}] {title}"),
|
||||
}
|
||||
let deps_suffix = progress.map(|(done, total)| format!(" ({done}/{total} deps done)"));
|
||||
let blocks_suffix = (blocking_open > 0).then(|| format!(" (blocks {blocking_open})"));
|
||||
println!(
|
||||
"#{number:>4} {kind} [{author}] {title}{}{}",
|
||||
deps_suffix.unwrap_or_default(),
|
||||
blocks_suffix.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
|
||||
/// Count of *open* issues `blocking` this row's issue blocks — closed
|
||||
/// followers don't count toward "actionable" (see [`print_row`]'s doc
|
||||
/// comment); they're already done regardless of this one's own state.
|
||||
fn blocking_open_count(blocking: &[Value]) -> usize {
|
||||
blocking
|
||||
.iter()
|
||||
.filter(|b| b.get("state").and_then(Value::as_str) == Some("open"))
|
||||
.count()
|
||||
}
|
||||
|
||||
/// Dependency completion progress from a `dependency_summaries` list —
|
||||
|
|
|
|||
Loading…
Reference in a new issue