//! `subscription [--watch|--ignore|--unwatch|--list] [repo]` — get or //! set the current user's watch subscription for a repo, or list every //! repo the user watches (`--list`, for auditing the notification //! firehose). use anyhow::Result; use clap::Args as ClapArgs; use serde_json::json; use crate::client::{Client, is_not_found}; use crate::verbs::print_json; #[derive(ClapArgs)] #[allow( clippy::struct_excessive_bools, reason = "mutually-exclusive clap action flags (group = \"action\"); an \ enum would drop the --watch/--ignore/--unwatch/--list flag \ ergonomics agents already use" )] pub struct Args { /// Subscribe (watch the repo). #[arg(long, group = "action")] watch: bool, /// Mute (mark ignored). #[arg(long, group = "action")] ignore: bool, /// Unsubscribe (clear watch + ignore). #[arg(long, group = "action")] unwatch: bool, /// List every repo the current user watches (ignores `[repo]`). #[arg(long, group = "action")] list: bool, } /// Run the `subscription` verb: `--list` all watched repos, set the /// current repo's watch (`--watch` / `--ignore` / `--unwatch`), or get /// its current state. /// /// # Errors /// Propagates transport / non-2xx errors from the Forgejo client calls /// and from `print_json`. pub fn run(client: &Client, args: Args) -> Result<()> { if args.list { // List every repo the authed user watches, so an agent can audit // its notification firehose and decide what to `--unwatch`. Not // repo-scoped. Paginated, with a generous page cap so a // misbehaving server can't spin us forever. const MAX_PAGES: u32 = 50; const PAGE: u32 = 50; let mut watching: Vec = Vec::new(); for page in 1..=MAX_PAGES { let (_, repos) = client .api() .user_current_list_subscriptions() .page(page) .page_size(PAGE) .send()?; let n = repos.len(); watching.extend(repos.into_iter().filter_map(|r| r.full_name)); if n < PAGE as usize { break; } } return print_json(&json!({ "watching": watching })); } let (owner, name) = client.owner_repo()?; if args.unwatch { client .api() .user_current_delete_subscription(owner, name) .send()?; println!("unsubscribed"); return Ok(()); } if args.watch || args.ignore { // Forgejo's PUT subscription endpoint takes no body — it always // subscribes (watch). The old raw client sent a // `{subscribed, ignored}` body which the server ignored, so // `--ignore` has always behaved like `--watch` server-side; the // typed call just makes that explicit. let resp = client .api() .user_current_put_subscription(owner, name) .send()?; return print_json(&json!({ "subscribed": resp.subscribed, "ignored": resp.ignored, })); } // Forgejo returns 404 when the current user is not watching the repo // (rather than a response with subscribed=false). match client .api() .user_current_check_subscription(owner, name) .send() { Ok(resp) => print_json(&json!({ "subscribed": resp.subscribed, "ignored": resp.ignored, })), Err(e) if is_not_found(&e) => print_json(&json!({ "subscribed": false, "ignored": false })), Err(e) => Err(e.into()), } }