diff --git a/hive-forge/src/verbs/pr_reviews.rs b/hive-forge/src/verbs/pr_reviews.rs index dbf95772..f19f1c16 100644 --- a/hive-forge/src/verbs/pr_reviews.rs +++ b/hive-forge/src/verbs/pr_reviews.rs @@ -1,6 +1,7 @@ -//! `pr-reviews [repo]` — list PR reviews with id/state/user/body. +//! `pr-reviews ` — list reviews, or submit one via `--approve` / +//! `--request-changes` / `--comment`. -use anyhow::Result; +use anyhow::{bail, Result}; use clap::Args as ClapArgs; use serde_json::{Value, json}; @@ -11,26 +12,76 @@ use crate::verbs::print_json; pub struct Args { /// PR number. number: u64, + + /// Approve the PR (submit an APPROVED review). + #[arg(long, conflicts_with_all = ["request_changes", "comment"])] + approve: bool, + + /// Request changes on the PR (submit a REQUEST_CHANGES review). + #[arg(long, conflicts_with_all = ["approve", "comment"])] + request_changes: bool, + + /// Leave a comment review (submit a COMMENT review). + #[arg(long, conflicts_with_all = ["approve", "request_changes"])] + comment: bool, + + /// Optional body / message for the review (used with --approve, + /// --request-changes, or --comment). + #[arg(long, short = 'm')] + body: Option, } pub fn run(client: &Client, args: Args) -> Result<()> { let repo = client.repo(); - let v = client.get_json(&format!("/repos/{repo}/pulls/{}/reviews", args.number))?; - let trimmed: Vec = v - .as_array() - .map(|a| { - a.iter() - .map(|r| { - json!({ - "id": r.get("id"), - "state": r.get("state"), - "user": r.get("user").and_then(|u| u.get("login")), - "body": r.get("body"), - "comments_count": r.get("comments_count"), + + let event = if args.approve { + Some("APPROVED") + } else if args.request_changes { + Some("REQUEST_CHANGES") + } else if args.comment { + Some("COMMENT") + } else { + None + }; + + if let Some(ev) = event { + // Submit a review. + let payload = json!({ + "event": ev, + "body": args.body.unwrap_or_default(), + }); + let v = client + .post_json(&format!("/repos/{repo}/pulls/{}/reviews", args.number), &payload)?; + // Print a compact summary rather than the full review blob. + let summary = json!({ + "id": v.get("id"), + "state": v.get("state"), + "user": v.get("user").and_then(|u| u.get("login")), + }); + print_json(&summary) + } else { + if args.body.is_some() { + bail!("--body requires one of --approve / --request-changes / --comment"); + } + // List mode (original behaviour). + let v = + client.get_json(&format!("/repos/{repo}/pulls/{}/reviews", args.number))?; + let trimmed: Vec = v + .as_array() + .map(|a| { + a.iter() + .map(|r| { + json!({ + "id": r.get("id"), + "state": r.get("state"), + "user": r.get("user").and_then(|u| u.get("login")), + "body": r.get("body"), + "comments_count": r.get("comments_count"), + }) }) - }) - .collect() - }) - .unwrap_or_default(); - print_json(&Value::Array(trimmed)) + .collect() + }) + .unwrap_or_default(); + print_json(&Value::Array(trimmed)) + } }