//! `comment [body sources] [repo]` — post a comment on an //! issue or PR. //! //! Read-before-comment guard: before posting, refuse when forge still //! has an unread notification for the thread — i.e. someone commented //! since the caller last read it. Reading the thread //! (`hive-forge comments ` / `view `) marks the notification read //! and clears the block; `--force` overrides. The unread signal is //! forge's own notification read-state, not a local mirror — see //! `crate::notify`. use anyhow::{Result, bail}; use clap::Args as ClapArgs; use serde_json::json; use crate::body; use crate::client::Client; use crate::notify; use crate::verbs::print_json; #[derive(ClapArgs)] pub struct Args { /// Issue or PR number. pub(crate) number: u64, /// Inline body text. #[arg(long, conflicts_with = "body_file")] body: Option, /// Read body from a file. `-` means stdin. #[arg(long = "body-file")] body_file: Option, /// Post even when the thread has unread activity (skips the /// read-before-comment guard). #[arg(long)] force: bool, } /// # Errors /// /// Returns an error when the read-before-comment guard fires (the thread /// has an unread notification and `--force` was not passed), and /// propagates any I/O error from the body input (`--body-file`, stdin), /// any transport error from the Forgejo REST call (network unreachable, /// 4xx/5xx response, token missing/invalid), and any I/O error from /// writing the response to stdout. pub fn run(client: &Client, args: Args) -> Result<()> { let body = body::resolve_required(args.body.as_deref(), args.body_file.as_deref(), "comment")?; let repo = client.repo(); if !args.force { // Degrade open: a transport failure checking notifications must // not block a legitimate comment — only a *confirmed* unread // thread refuses. match notify::unread_thread_id(client, repo, args.number) { Ok(Some(_)) => bail!( "hive-forge: {repo}#{0} has unread activity — someone commented since you last \ read it. Read it first (`hive-forge comments {0}`), then retry — or pass --force.", args.number ), Ok(None) => {} Err(e) => eprintln!( "hive-forge: warning: could not check notifications ({e}); posting anyway" ), } } let resp = client.post_json( &format!("/repos/{repo}/issues/{}/comments", args.number), &json!({ "body": body }), )?; print_json(&json!({ "id": resp.get("id"), "url": resp.get("html_url"), })) }