//! `attach-issue ` and `attach-comment //! ` — upload a file as an attachment. Prints the browser //! download URL. use std::path::PathBuf; use anyhow::{Result, bail}; use clap::Args as ClapArgs; use serde_json::Value; use crate::client::Client; #[derive(ClapArgs)] pub struct IssueArgs { /// Issue number. number: u64, /// File path to upload. file: PathBuf, } #[derive(ClapArgs)] pub struct CommentArgs { /// Comment id. id: u64, /// File path to upload. file: PathBuf, } /// # Errors /// /// Returns an error if the input file doesn't exist or can't be /// read. Propagates any transport error from the Forgejo REST call /// (network unreachable, 4xx/5xx response, token missing/invalid) /// and any I/O error from writing the browser download URL to stdout. pub fn run_issue(client: &Client, args: IssueArgs) -> Result<()> { if !args.file.is_file() { bail!( "hive-forge attach-issue: file not found: {}", args.file.display() ); } let repo = client.repo(); let resp = client.post_multipart_file( &format!("/repos/{repo}/issues/{}/assets", args.number), &args.file, )?; print_url(&resp); Ok(()) } /// # Errors /// /// Returns an error if the input file doesn't exist or can't be /// read. Propagates any transport error from the Forgejo REST call /// (network unreachable, 4xx/5xx response, token missing/invalid) /// and any I/O error from writing the browser download URL to stdout. pub fn run_comment(client: &Client, args: CommentArgs) -> Result<()> { if !args.file.is_file() { bail!( "hive-forge attach-comment: file not found: {}", args.file.display() ); } let repo = client.repo(); let resp = client.post_multipart_file( &format!("/repos/{repo}/issues/comments/{}/assets", args.id), &args.file, )?; print_url(&resp); Ok(()) } fn print_url(v: &Value) { if let Some(url) = v.get("browser_download_url").and_then(Value::as_str) { println!("{url}"); } }