dotfiles/agent/src/server/exec.rs

62 lines
1.6 KiB
Rust
Raw Normal View History

2023-06-13 13:02:49 +00:00
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),
};
}