61 lines
1.6 KiB
Rust
61 lines
1.6 KiB
Rust
use std::{ffi::OsStr, process::Stdio};
|
|
|
|
use tokio::{
|
|
io::{AsyncBufReadExt, BufReader},
|
|
process::{Child, Command},
|
|
sync::mpsc,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub(super) struct ExecOutput {
|
|
pub(super) stdout: Option<String>,
|
|
pub(super) stderr: Option<String>,
|
|
}
|
|
|
|
pub(super) fn exec<S>(program: &S, args: &[S]) -> anyhow::Result<mpsc::Receiver<ExecOutput>>
|
|
where
|
|
S: AsRef<OsStr>,
|
|
{
|
|
let (tx, rx) = mpsc::channel(4);
|
|
|
|
let command = Command::new(program)
|
|
.args(args)
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?;
|
|
|
|
tokio::spawn(async move { run_command(command, tx).await });
|
|
|
|
Ok(rx)
|
|
}
|
|
|
|
async fn run_command(mut command: Child, tx: mpsc::Sender<ExecOutput>) {
|
|
let mut stdout = {
|
|
let stdout = command.stdout.take().expect("bug: no pipe for stdout");
|
|
BufReader::new(stdout).lines()
|
|
};
|
|
|
|
let mut stderr = {
|
|
let stderr = command.stderr.take().expect("bug: no pipe for stderr");
|
|
BufReader::new(stderr).lines()
|
|
};
|
|
|
|
loop {
|
|
match (stdout.next_line().await, stderr.next_line().await) {
|
|
// TODO: Handle errors
|
|
(Err(_err), _) | (_, Err(_err)) => break,
|
|
(stdout, stderr) => tx
|
|
.send(ExecOutput {
|
|
stdout: stdout.unwrap(),
|
|
stderr: stderr.unwrap(),
|
|
})
|
|
.await
|
|
.expect("bug: channel closed"),
|
|
}
|
|
}
|
|
|
|
match command.wait().await {
|
|
Ok(exit_status) => println!("exit: {}", exit_status),
|
|
Err(err) => panic!("errorrrrr {}", err),
|
|
};
|
|
}
|