hive-forge: comments --tail N flag (#694 part 3) — last N comments in chronological order
This commit is contained in:
parent
f7e38c0b42
commit
5f4494c239
1 changed files with 71 additions and 26 deletions
|
|
@ -1,6 +1,17 @@
|
|||
//! `comments <number> [--limit N]` — list all comments on an issue
|
||||
//! or PR. Closes the curl-fallback gap (#418). Use the global
|
||||
//! `--json` flag for JSON output (#421).
|
||||
//! `comments <number> [--limit N | --tail N]` — list comments on an
|
||||
//! issue or PR. Closes the curl-fallback gap (#418); `--tail`
|
||||
//! closes the third of the four #694 gaps (paging-for-long-threads
|
||||
//! awkwardness).
|
||||
//!
|
||||
//! - `--limit N` (default 50, Forgejo's cap) returns the first N
|
||||
//! comments — same shape this verb has always had.
|
||||
//! - `--tail N` returns the *last* N comments in chronological
|
||||
//! order. Internally pages through every comment on the issue,
|
||||
//! then slices the tail. Use this for "what was the conclusion
|
||||
//! on this long thread?" without scrolling through the whole
|
||||
//! history.
|
||||
//!
|
||||
//! Use the global `--json` flag for JSON output (#421).
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args as ClapArgs;
|
||||
|
|
@ -9,42 +20,52 @@ use serde_json::{Value, json};
|
|||
use crate::client::Client;
|
||||
use crate::verbs::print_json;
|
||||
|
||||
/// Hard cap on `--tail` pagination so a confused caller can't
|
||||
/// accidentally paginate forever on a runaway-comment thread.
|
||||
/// 20 pages × Forgejo's 50-per-page cap = 1000 comments which
|
||||
/// covers every issue in the hyperhive repo today with headroom.
|
||||
/// Tune up if a future thread breaches it (the truncated tail
|
||||
/// will surface as a smaller-than-expected slice).
|
||||
const TAIL_MAX_PAGES: u32 = 20;
|
||||
|
||||
#[derive(ClapArgs)]
|
||||
pub struct Args {
|
||||
/// Issue or PR number.
|
||||
number: u64,
|
||||
/// Page size (Forgejo caps at 50 by default).
|
||||
#[arg(long, default_value_t = 50)]
|
||||
/// Page size for the head-of-thread shape (Forgejo caps at 50).
|
||||
/// Mutually exclusive with `--tail`.
|
||||
#[arg(long, default_value_t = 50, conflicts_with = "tail")]
|
||||
limit: u64,
|
||||
/// Return the last `N` comments in chronological order. Pages
|
||||
/// through every comment on the issue under the hood, then
|
||||
/// slices the tail. Mutually exclusive with `--limit`.
|
||||
#[arg(long)]
|
||||
tail: Option<usize>,
|
||||
}
|
||||
|
||||
pub fn run(client: &Client, args: Args) -> Result<()> {
|
||||
let repo = client.repo();
|
||||
let v = client.get_json(&format!(
|
||||
"/repos/{repo}/issues/{}/comments?limit={}",
|
||||
args.number, args.limit
|
||||
))?;
|
||||
let comments = match args.tail {
|
||||
Some(n) => fetch_tail(client, &repo, args.number, n)?,
|
||||
None => fetch_head(client, &repo, args.number, args.limit)?,
|
||||
};
|
||||
if client.json_mode() {
|
||||
let trimmed: Vec<Value> = v
|
||||
.as_array()
|
||||
.map(|a| {
|
||||
a.iter()
|
||||
.map(|c| {
|
||||
json!({
|
||||
"id": c.get("id"),
|
||||
"user": c.get("user").and_then(|u| u.get("login")),
|
||||
"created_at": c.get("created_at"),
|
||||
"updated_at": c.get("updated_at"),
|
||||
"body": c.get("body"),
|
||||
"url": c.get("html_url"),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
let trimmed: Vec<Value> = comments
|
||||
.iter()
|
||||
.map(|c| {
|
||||
json!({
|
||||
"id": c.get("id"),
|
||||
"user": c.get("user").and_then(|u| u.get("login")),
|
||||
"created_at": c.get("created_at"),
|
||||
"updated_at": c.get("updated_at"),
|
||||
"body": c.get("body"),
|
||||
"url": c.get("html_url"),
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
.collect();
|
||||
print_json(&Value::Array(trimmed))
|
||||
} else {
|
||||
for c in v.as_array().cloned().unwrap_or_default() {
|
||||
for c in &comments {
|
||||
let user = c
|
||||
.get("user")
|
||||
.and_then(|u| u.get("login"))
|
||||
|
|
@ -58,3 +79,27 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the first page's worth of comments (existing behaviour).
|
||||
fn fetch_head(client: &Client, repo: &str, number: u64, limit: u64) -> Result<Vec<Value>> {
|
||||
let v = client.get_json(&format!(
|
||||
"/repos/{repo}/issues/{number}/comments?limit={limit}"
|
||||
))?;
|
||||
Ok(v.as_array().cloned().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Fetch every comment via paginated GET, then return the last `n`
|
||||
/// items in chronological order. Forgejo orders
|
||||
/// `/issues/<n>/comments` oldest-first so the trailing slice maps
|
||||
/// directly to "most recent N".
|
||||
fn fetch_tail(client: &Client, repo: &str, number: u64, n: usize) -> Result<Vec<Value>> {
|
||||
let all = client.get_json_all(
|
||||
&format!("/repos/{repo}/issues/{number}/comments?limit=50"),
|
||||
TAIL_MAX_PAGES,
|
||||
)?;
|
||||
let total = all.len();
|
||||
if total <= n {
|
||||
return Ok(all);
|
||||
}
|
||||
Ok(all.into_iter().skip(total - n).collect())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue