use std::{
io,
process::{self, Command},
};
pub(crate) trait Sealed {}
impl Sealed for Command {}
#[allow(private_bounds)]
pub trait CommandExt: Sealed {
#[must_use]
fn cross_exec(&mut self) -> io::Error;
}
impl CommandExt for Command {
#[allow(unreachable_code)]
fn cross_exec(&mut self) -> io::Error {
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
return self.exec();
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
use windows::{Win32::System::Console::SetConsoleCtrlHandler, core::BOOL};
unsafe extern "system" fn ignore_all(_: u32) -> BOOL {
true.into()
}
let res = unsafe { SetConsoleCtrlHandler(Some(ignore_all), true.into()) };
if let Err(e) = res {
return io::Error::new(
io::ErrorKind::Other,
format!("failed to set Ctrl+C handler: {}", e),
);
}
}
let res = self.status();
let status = match res {
Ok(s) => s,
Err(e) => return e,
};
process::exit(status.code().unwrap_or(128));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn test_example_cargo_wrapper() -> Result<(), Box<dyn Error>> {
let expected = {
let output = Command::new("cargo").arg("--version").output()?;
let stdout = String::from_utf8(output.stdout)?.replace("\r\n", "\n");
format!("Hello from before cross_exec!\n{}", stdout)
};
let actual = {
let output = Command::new("cargo")
.args(&["run", "--example", "cargo-wrapper", "--", "--version"])
.output()?;
String::from_utf8(output.stdout)?.replace("\r\n", "\n")
};
assert_eq!(expected, actual);
Ok(())
}
}