use std::fs;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::process::{Command, Stdio};
use super::error::create_hook_error;
use super::process::handle_process_output;
use crate::bgit_error::BGitError;
fn create_command_for_hook(hook_path: &Path) -> Option<Command> {
let extension = hook_path.extension()?.to_str()?;
match extension {
"ps1" => {
let mut cmd = Command::new("powershell");
cmd.args(["-ExecutionPolicy", "Bypass", "-File", hook_path.to_str()?]);
Some(cmd)
}
"bat" | "cmd" => {
let mut cmd = Command::new("cmd");
cmd.args(["/C", hook_path.to_str()?]);
Some(cmd)
}
"exe" => Some(Command::new(hook_path)),
_ => Some(Command::new(hook_path)),
}
}
fn try_bash_execution(hook_path: &Path) -> Option<Command> {
let file = fs::File::open(hook_path).ok()?;
let mut reader = BufReader::new(file);
let mut first_line = String::new();
let _ = reader.read_line(&mut first_line);
if !first_line.contains("bash") {
return None;
}
let bash_paths = [
"C:\\Program Files\\Git\\bin\\bash.exe",
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
"C:\\msys64\\usr\\bin\\bash.exe",
"bash.exe", ];
for &bash_path in &bash_paths {
if Path::new(bash_path).exists() || bash_path == "bash.exe" {
let mut cmd = Command::new(bash_path);
cmd.arg(hook_path.to_str()?);
return Some(cmd);
}
}
if Command::new("wsl").arg("--version").output().is_ok() {
let mut cmd = Command::new("wsl");
cmd.arg(hook_path.to_str()?);
return Some(cmd);
}
None
}
pub fn execute_hook_util(event_hook_path: &Path, event_name: &str) -> Result<bool, Box<BGitError>> {
if !event_hook_path.exists() {
return Ok(true);
}
let mut command = match create_command_for_hook(event_hook_path) {
Some(cmd) => cmd,
None => {
match try_bash_execution(event_hook_path) {
Some(cmd) => cmd,
None => {
let mut cmd = Command::new("cmd");
cmd.args([
"/C",
event_hook_path.to_str().ok_or_else(|| {
create_hook_error(
"Invalid path",
"Path contains invalid characters",
event_name,
)
})?,
]);
cmd
}
}
}
};
command.stdout(Stdio::piped());
command.stderr(Stdio::piped());
let mut child = command
.spawn()
.map_err(|e| create_hook_error("Failed to execute hook", &e.to_string(), event_name))?;
handle_process_output(&mut child)?;
let status = child.wait().map_err(|e| {
create_hook_error(
"Failed to wait for hook execution",
&e.to_string(),
event_name,
)
})?;
if status.success() {
Ok(true)
} else {
Err(create_hook_error(
"event-hook failed",
&format!(
"Hook for {} failed with exit code: {:?}",
event_name,
status.code()
),
event_name,
))
}
}