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, pub(super) stderr: Option, } pub(super) fn exec(program: &S, args: &[S]) -> anyhow::Result> where S: AsRef, { 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) { 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), }; }