use crate::github::domain::types::*;
use std::process::Command;
fn run_gh(args: &[&str], context: &str) -> Result<Vec<u8>, String> {
let output = Command::new("gh")
.args(args)
.output()
.map_err(|e| format!("{context}: {e}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(stderr.trim().to_string());
}
Ok(output.stdout)
}
fn run_gh_json<T: serde::de::DeserializeOwned>(args: &[&str], context: &str) -> Result<T, String> {
let stdout = run_gh(args, context)?;
serde_json::from_slice(&stdout).map_err(|e| format!("JSON parse error: {e}"))
}
fn open_in_browser(entity: &str, number: u64) -> Result<(), String> {
Command::new("gh")
.args([entity, "view", &number.to_string(), "--web"])
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.map_err(|e| format!("Failed to open {entity} in browser: {e}"))?;
Ok(())
}
pub fn check_gh_available() -> Result<(), String> {
run_gh(&["auth", "status"], "gh not found").map(|_| ())
}
pub fn list_issues(limit: usize) -> Result<Vec<GhIssueListItem>, String> {
run_gh_json(
&[
"issue",
"list",
"--json",
"number,title,state,author,labels,createdAt",
"--limit",
&limit.to_string(),
],
"gh issue list failed",
)
}
pub fn list_prs(limit: usize) -> Result<Vec<GhPrListItem>, String> {
run_gh_json(
&[
"pr",
"list",
"--json",
"number,title,state,author,labels,headRefName,createdAt,reviewDecision,isDraft",
"--limit",
&limit.to_string(),
],
"gh pr list failed",
)
}
pub fn get_issue(number: u64) -> Result<GhIssueDetail, String> {
run_gh_json(
&[
"issue",
"view",
&number.to_string(),
"--json",
"number,title,state,author,body,comments,labels,createdAt",
],
"gh issue view failed",
)
}
pub fn get_pr(number: u64) -> Result<GhPrDetail, String> {
run_gh_json(
&[
"pr",
"view",
&number.to_string(),
"--json",
"number,title,state,author,body,comments,reviews,labels,createdAt,reviewDecision,statusCheckRollup,additions,deletions,changedFiles,headRefName",
],
"gh pr view failed",
)
}
pub fn open_issue_in_browser(number: u64) -> Result<(), String> {
open_in_browser("issue", number)
}
pub fn open_pr_in_browser(number: u64) -> Result<(), String> {
open_in_browser("pr", number)
}
pub fn repo_nwo() -> Option<String> {
let stdout = run_gh(
&[
"repo",
"view",
"--json",
"nameWithOwner",
"-q",
".nameWithOwner",
],
"gh repo view",
)
.ok()?;
Some(String::from_utf8_lossy(&stdout).trim().to_string())
}