85 lines
2.9 KiB
Rust
85 lines
2.9 KiB
Rust
//! `repo-search [--query <kw>] [--topic] [--include-desc] [--limit N]` —
|
|
//! search for repositories on the forge instance.
|
|
//!
|
|
//! Wraps `GET /repos/search` (Forgejo explore endpoint). Useful for agents
|
|
//! that need to find a repo by keyword rather than knowing the exact
|
|
//! `owner/repo` slug upfront. Not repo-scoped — the `-r` flag is ignored.
|
|
//!
|
|
//! Human output: one `owner/repo description` line per hit. `--json`
|
|
//! emits the full `SearchResults.data` array as raw JSON.
|
|
|
|
use anyhow::Result;
|
|
use clap::Args as ClapArgs;
|
|
use forgejo_api::structs::RepoSearchQuery;
|
|
|
|
use crate::client::Client;
|
|
use crate::verbs::print_json;
|
|
|
|
#[derive(ClapArgs)]
|
|
pub struct Args {
|
|
/// Keyword to search for (matches repo name by default; combine with
|
|
/// `--include-desc` to also match description text).
|
|
#[arg(short = 'q', long)]
|
|
query: Option<String>,
|
|
/// Restrict matches to repositories that have the keyword as a **topic**
|
|
/// tag rather than in the name.
|
|
#[arg(long)]
|
|
topic: bool,
|
|
/// Extend the keyword search to repository descriptions (in addition to
|
|
/// names, or topics when `--topic` is set).
|
|
#[arg(long = "include-desc")]
|
|
include_desc: bool,
|
|
/// Maximum number of results to return (default: 30).
|
|
#[arg(long, default_value_t = 30, value_parser = clap::value_parser!(u64).range(1..))]
|
|
limit: u64,
|
|
}
|
|
|
|
/// # Errors
|
|
///
|
|
/// Propagates transport errors from the Forgejo search call or JSON
|
|
/// serialisation errors when emitting `--json` output.
|
|
pub fn run(client: &Client, args: Args) -> Result<()> {
|
|
let limit = u32::try_from(args.limit.min(u64::from(u32::MAX))).unwrap_or(u32::MAX);
|
|
let results = client
|
|
.api()
|
|
.repo_search(RepoSearchQuery {
|
|
q: args.query,
|
|
topic: args.topic.then_some(true),
|
|
include_desc: args.include_desc.then_some(true),
|
|
// All other filters left at defaults — callers who need them
|
|
// can drop to `--json` and post-filter.
|
|
uid: None,
|
|
priority_owner_id: None,
|
|
team_id: None,
|
|
starred_by: None,
|
|
private: None,
|
|
is_private: None,
|
|
template: None,
|
|
archived: None,
|
|
mode: None,
|
|
exclusive: None,
|
|
sort: None,
|
|
order: None,
|
|
})
|
|
.page_size(limit)
|
|
.send()?;
|
|
|
|
let repos = results.data.unwrap_or_default();
|
|
if client.json_mode() {
|
|
return print_json(&serde_json::to_value(&repos)?);
|
|
}
|
|
if repos.is_empty() {
|
|
eprintln!("(no results)");
|
|
return Ok(());
|
|
}
|
|
for repo in &repos {
|
|
let full_name = repo.full_name.as_deref().unwrap_or("?/?");
|
|
let desc = repo.description.as_deref().unwrap_or("");
|
|
if desc.is_empty() {
|
|
println!("{full_name}");
|
|
} else {
|
|
println!("{full_name} {desc}");
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|