use std::convert::Infallible;
use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle};
use std::os::windows::process::CommandExt;
use std::process::{Child, Command};
use windows::Win32::Foundation::{HANDLE, WAIT_OBJECT_0};
use windows::Win32::System::Threading::{
CREATE_NO_WINDOW, GetExitCodeProcess, INFINITE, WaitForSingleObject,
};
use crate::job::JobError;
use crate::{Job, install_ctrl_handler};
struct SupervisedChild<'a> {
_job: Job,
handle: BorrowedHandle<'a>,
}
#[allow(unsafe_code)]
impl<'a> SupervisedChild<'a> {
fn new(child: &'a Child) -> Result<Self, JobError> {
let job = Job::new()?;
let handle = child.as_handle();
unsafe { job.assign_process(HANDLE(handle.as_raw_handle())) }?;
Ok(Self { _job: job, handle })
}
fn raw_handle(&self) -> HANDLE {
HANDLE(self.handle.as_raw_handle())
}
}
#[allow(unsafe_code)]
pub fn spawn_child(cmd: &mut Command, hide_console: bool) -> std::io::Result<Infallible> {
cmd.stdin(std::process::Stdio::inherit());
if hide_console {
cmd.creation_flags(CREATE_NO_WINDOW.0);
}
let child = cmd.spawn()?;
let supervised =
SupervisedChild::new(&child).map_err(|e| std::io::Error::other(e.to_string()))?;
let _ = install_ctrl_handler();
let wait_result = unsafe { WaitForSingleObject(supervised.raw_handle(), INFINITE) };
if wait_result != WAIT_OBJECT_0 {
return Err(std::io::Error::other(format!(
"WaitForSingleObject failed with result: {wait_result:?}"
)));
}
let mut exit_code = 0u32;
unsafe { GetExitCodeProcess(supervised.raw_handle(), &raw mut exit_code) }
.map_err(|e| std::io::Error::other(format!("Failed to get exit code: {e}")))?;
#[allow(clippy::exit, clippy::cast_possible_wrap)]
std::process::exit(exit_code as i32)
}