use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};
use crate::error::{CwError, Result};
#[derive(serde::Deserialize)]
struct CreatePayload {
#[serde(alias = "name")]
worktree_name: String,
#[serde(alias = "base_ref")]
base_commit: Option<String>,
worktree_path: Option<String>,
cwd: Option<String>,
#[serde(flatten)]
_other: serde_json::Value,
}
#[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 = payload.worktree_name.trim();
if branch.is_empty() {
return Err(CwError::Other(
"WorktreeCreate hook payload had an empty worktree_name/name".into(),
));
}
let exe = std::env::current_exe()?;
let mut args = vec!["new".to_string(), branch.to_string()];
if let Some(path) = payload.worktree_path.as_deref().filter(|p| !p.is_empty()) {
args.push("--path".to_string());
args.push(path.to_string());
}
if let Some(base) = payload.base_commit.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(())
}