use std::convert::From;
#[cfg(target_family = "windows")]
use std::os::windows::io::AsRawHandle;
use std::process::Child;
use std::process::ExitStatus;
#[cfg(target_family = "windows")]
use std::thread::sleep;
#[cfg(target_family = "windows")]
use std::time::Duration;
use thiserror::Error;
#[cfg(target_family = "windows")]
use win32job::Job;
#[cfg(target_family = "windows")]
use win32job::JobError;
#[cfg(target_family = "windows")]
const PROCESS_POLLING_INTERVAL: u64 = 1000;
#[derive(Debug, Error)]
pub enum ChildExtError {
#[error("error executing external application")]
Wait(#[from] std::io::Error),
#[cfg(target_family = "windows")]
#[error("can not monitor the termination of the launched application")]
Monitor(#[from] JobError),
#[allow(dead_code)]
#[cfg(not(target_family = "windows"))]
#[error("can not monitor the termination of the launched application")]
Monitor,
}
#[derive(Debug)]
pub struct ChildExt(Child);
impl From<Child> for ChildExt {
fn from(inner: Child) -> Self {
Self(inner)
}
}
impl ChildExt {
#[cfg(not(target_family = "windows"))]
#[inline]
pub fn wait(&mut self) -> Result<ExitStatus, ChildExtError> {
let process_id = self.0.id();
log::debug!("Process started: id={}", process_id);
let exit_status = self.0.wait();
log::debug!(
"Process terminated: id={}, {}",
process_id,
match &exit_status {
Ok(ex_st) => ex_st.to_string(),
Err(e) => e.to_string(),
}
);
Ok(exit_status?)
}
#[cfg(target_family = "windows")]
pub fn wait(&mut self) -> Result<ExitStatus, ChildExtError> {
let process_id = self.0.id();
log::debug!("Process started: id={}", process_id);
let job = Job::create()?;
let handle = self.0.as_raw_handle();
job.assign_process(handle)?;
let exit_status = self.0.wait()?;
log::debug!("Process terminated: id={}, {}", process_id, exit_status);
if !exit_status.success() {
return Ok(exit_status);
};
let ids = job.query_process_id_list()?;
if ids.len() > 0 {
log::debug!(
"Processes id={} launched still running ids:{:?}.",
process_id,
ids
);
}
while job.query_process_id_list()?.len() > 0 {
sleep(Duration::from_millis(PROCESS_POLLING_INTERVAL));
}
if ids.len() > 0 {
log::debug!("All processes launched by id={} terminated.", process_id);
};
Ok(exit_status)
}
}