use std::{
io,
path::{Path, PathBuf},
process::{ExitStatus, Stdio},
};
use tokio::process::{Child, Command};
use super::port::Address;
pub struct AppServer {
child: Child,
}
impl AppServer {
pub fn spawn(exe: &Path, dev_url: &str, address: &Address) -> io::Result<Self> {
let exe = shadow_copy_for_windows(exe)?;
let child = Command::new(exe)
.env("TOPCOAT_DEV_URL", dev_url)
.env("HOST", &address.host)
.env("PORT", address.port.to_string())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.stdin(Stdio::inherit())
.kill_on_drop(true)
.spawn()?;
Ok(Self { child })
}
pub async fn exited(&mut self) -> io::Result<ExitStatus> {
self.child.wait().await
}
pub async fn shutdown(mut self) {
let _ = self.child.kill().await;
let _ = self.child.wait().await;
}
}
fn shadow_copy_for_windows(exe: &Path) -> io::Result<PathBuf> {
if !cfg!(windows) {
return Ok(exe.to_path_buf());
}
let mut file_name = exe.file_stem().unwrap_or_default().to_os_string();
file_name.push(".topcoat-dev");
if let Some(extension) = exe.extension() {
file_name.push(".");
file_name.push(extension);
}
let shadow = exe.with_file_name(file_name);
let mut last_error = None;
for _ in 0..20 {
match std::fs::copy(exe, &shadow) {
Ok(_) => return Ok(shadow),
Err(error) => last_error = Some(error),
}
std::thread::sleep(std::time::Duration::from_millis(50));
}
Err(last_error.expect("copy attempted at least once"))
}