use std::{
future::Future,
io,
pin::Pin,
process::{Child, Command, ExitStatus, Stdio},
task::{Context, Poll, ready},
time::Duration,
};
use zbus::runtime::traits;
use super::{Handle, Sleep};
pub(super) fn spawn(
handle: &Handle,
mut command: Command,
stdin: Stdio,
stdout: Stdio,
stderr: Stdio,
) -> io::Result<Pin<Box<dyn Future<Output = io::Result<ExitStatus>> + Send + 'static>>> {
let child = command.stdin(stdin).stdout(stdout).stderr(stderr).spawn();
drop(command);
Ok(Box::pin(Exit {
handle: handle.clone(),
child: Some(child?),
sleep: None,
}))
}
const INTERVAL: Duration = Duration::from_millis(10);
struct Exit {
handle: Handle,
child: Option<Child>,
sleep: Option<Sleep>,
}
impl Future for Exit {
type Output = io::Result<ExitStatus>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
loop {
if let Some(sleep) = this.sleep.as_mut() {
ready!(Pin::new(sleep).poll(cx));
this.sleep = None;
}
let child = this
.child
.as_mut()
.expect("the exit status of a child process was polled after it resolved");
match child.try_wait() {
Ok(Some(status)) => {
this.child = None;
return Poll::Ready(Ok(status));
}
Err(e) => {
this.child = None;
return Poll::Ready(Err(e));
}
Ok(None) => this.sleep = Some(traits::Runtime::sleep(&this.handle, INTERVAL)),
}
}
}
}