use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};
use crate::error::{CwError, Result};
#[derive(serde::Deserialize)]
struct CreatePayload {
worktree_path: String,
base_ref: Option<String>,
cwd: Option<String>,
#[serde(flatten)]
_other: serde_json::Value,
}
fn branch_from_worktree_path(worktree_path: &str) -> Result<String> {
Path::new(worktree_path)
.file_name()
.and_then(|s| s.to_str())
.filter(|s| !s.is_empty())
.map(str::to_string)
.ok_or_else(|| {
CwError::Other(format!(
"could not derive a branch name from worktree_path '{worktree_path}'"
))
})
}
#[derive(serde::Deserialize)]
struct RemovePayload {
worktree_path: String,
#[serde(flatten)]
_other: serde_json::Value,
}
pub fn run_create() -> Result<()> {
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
let payload: CreatePayload = serde_json::from_str(&buf)
.map_err(|e| CwError::Other(format!("invalid WorktreeCreate hook payload: {e}")))?;
if let Some(ref cwd) = payload.cwd {
std::env::set_current_dir(cwd)?;
}
let branch = branch_from_worktree_path(&payload.worktree_path)?;
let exe = std::env::current_exe()?;
let mut args = vec![
"new".to_string(),
branch,
"--path".to_string(),
payload.worktree_path.clone(),
];
if let Some(base) = payload.base_ref.as_deref().filter(|b| !b.is_empty()) {
args.push("--base".to_string());
args.push(base.to_string());
}
args.extend(["--emit", "json", "--term", "skip"].map(str::to_string));
let output = Command::new(&exe)
.args(&args)
.env("CW_NON_INTERACTIVE", "1")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.output()?;
if !output.status.success() {
return Err(CwError::Other(format!(
"gw new failed (exit {})",
output.status.code().unwrap_or(-1)
)));
}
let stdout = String::from_utf8_lossy(&output.stdout);
let parsed: serde_json::Value = serde_json::from_str(stdout.trim())
.map_err(|e| CwError::Other(format!("could not parse `gw new --emit json` output: {e}")))?;
let wt_path = parsed["worktree_path"]
.as_str()
.ok_or_else(|| CwError::Other("`worktree_path` missing in `gw new` JSON output".into()))?;
println!("{}", wt_path);
Ok(())
}
pub fn run_remove() -> Result<()> {
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
let payload: RemovePayload = serde_json::from_str(&buf)
.map_err(|e| CwError::Other(format!("invalid WorktreeRemove hook payload: {e}")))?;
let wt_path = Path::new(&payload.worktree_path);
if let Err(e) = crate::hooks::run_event("pre_rm", wt_path) {
eprintln!(
"warning: pre_rm hook failed for '{}' (advisory, removal proceeds): {}",
wt_path.display(),
e
);
}
Ok(())
}