hyperhive/hive-forge/src/verbs/repo_labels.rs

70 lines
2.3 KiB
Rust

//! `repo-labels [pattern]` — list the active repo's full label set
//! (project-wide), not the labels on one issue/PR (that's `labels
//! <number>`). Useful for triage / labelling to discover the valid label
//! names + what each means before applying them.
use anyhow::Result;
use clap::Args as ClapArgs;
use forgejo_api::structs::{IssueListLabelsQuery, Label};
use crate::client::Client;
use crate::verbs::print_json;
/// Page size on the label list endpoint.
const PAGE_SIZE: u32 = 50;
/// Runaway cap on label pagination — same ceiling the raw client used.
const MAX_PAGES: u32 = 10;
#[derive(ClapArgs)]
pub struct Args {
/// Substring pattern to filter label names (case-sensitive).
pattern: Option<String>,
}
pub fn run(client: &Client, args: Args) -> Result<()> {
let (owner, name) = client.owner_repo()?;
// Repos can carry more than one page of labels; paginate so the list
// is complete rather than capped at the first page.
let mut labels: Vec<Label> = Vec::new();
for page in 1..=MAX_PAGES {
let (_, batch) = client
.api()
.issue_list_labels(owner, name, IssueListLabelsQuery::default())
.page(page)
.page_size(PAGE_SIZE)
.send()?;
let short = batch.len() < PAGE_SIZE as usize;
labels.extend(batch);
if short {
break;
}
}
let filtered: Vec<&Label> = labels
.iter()
.filter(|l| {
let label_name = l.name.as_deref().unwrap_or_default();
args.pattern
.as_deref()
.is_none_or(|p| label_name.contains(p))
})
.collect();
if client.json_mode() {
// Full label objects (id, name, color, description) as a JSON array.
print_json(&serde_json::to_value(&filtered)?)?;
} else {
// One label per line: `name`, plus its description (tab-separated)
// when set, so triage can see what each label means at a glance.
for l in filtered {
let label_name = l.name.as_deref().unwrap_or_default();
let desc = l.description.as_deref().unwrap_or_default();
if desc.is_empty() {
println!("{label_name}");
} else {
println!("{label_name}\t{desc}");
}
}
}
Ok(())
}