62 lines
1.6 KiB
Rust
62 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),
|
||
|
};
|
||
|
}
|