use std::process::Command;
#[derive(Debug, Clone, Default)]
pub struct PrCreateState {
pub title: String,
pub body: String,
pub base_branch: String,
pub gh_available: bool,
pub cursor_pos: usize,
pub editing_body: bool,
}
pub fn check_gh_available() -> bool {
Command::new("which")
.arg("gh")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
pub fn create_pr(state: &PrCreateState) -> Result<String, String> {
if !state.gh_available {
return Err("gh CLI is not available. Install it from https://cli.github.com/".to_string());
}
if state.title.trim().is_empty() {
return Err("PR title cannot be empty".to_string());
}
let output = Command::new("gh")
.args([
"pr",
"create",
"--title",
&state.title,
"--body",
&state.body,
"--base",
&state.base_branch,
])
.output()
.map_err(|e| format!("Failed to execute gh: {}", e))?;
if output.status.success() {
let url = String::from_utf8_lossy(&output.stdout).trim().to_string();
Ok(url)
} else {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
Err(format!("gh pr create failed: {}", stderr))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pr_create_state_default() {
let state = PrCreateState::default();
assert!(state.title.is_empty());
assert!(state.body.is_empty());
assert!(state.base_branch.is_empty());
assert!(!state.gh_available);
assert_eq!(state.cursor_pos, 0);
assert!(!state.editing_body);
}
#[test]
fn test_pr_create_state_clone() {
let state = PrCreateState {
title: "My PR".to_string(),
body: "Description".to_string(),
base_branch: "main".to_string(),
gh_available: true,
cursor_pos: 5,
editing_body: true,
};
let cloned = state.clone();
assert_eq!(cloned.title, "My PR");
assert_eq!(cloned.body, "Description");
assert_eq!(cloned.base_branch, "main");
assert!(cloned.gh_available);
assert_eq!(cloned.cursor_pos, 5);
assert!(cloned.editing_body);
}
#[test]
fn test_create_pr_empty_title() {
let state = PrCreateState {
gh_available: true,
title: "".to_string(),
..Default::default()
};
let result = create_pr(&state);
assert!(result.is_err());
assert!(result.unwrap_err().contains("empty"));
}
#[test]
fn test_create_pr_whitespace_title() {
let state = PrCreateState {
gh_available: true,
title: " ".to_string(),
..Default::default()
};
let result = create_pr(&state);
assert!(result.is_err());
assert!(result.unwrap_err().contains("empty"));
}
#[test]
fn test_create_pr_no_gh() {
let state = PrCreateState {
gh_available: false,
title: "test PR".to_string(),
..Default::default()
};
let result = create_pr(&state);
assert!(result.is_err());
assert!(result.unwrap_err().contains("not available"));
}
#[test]
fn test_check_gh_available_returns_bool() {
let _result = check_gh_available();
}
}