use std::process::ExitStatus; use mlua::prelude::*; use tokio::{ io::{self, AsyncRead, AsyncReadExt}, process::Child, task, }; use super::{options::ProcessSpawnOptionsStdioKind, tee_writer::AsyncTeeWriter}; #[derive(Debug, Clone)] pub(super) struct WaitForChildResult { pub status: ExitStatus, pub stdout: Vec, pub stderr: Vec, } async fn read_with_stdio_kind( read_from: Option, kind: ProcessSpawnOptionsStdioKind, ) -> LuaResult> where R: AsyncRead + Unpin, { Ok(match kind { ProcessSpawnOptionsStdioKind::None | ProcessSpawnOptionsStdioKind::Forward => Vec::new(), ProcessSpawnOptionsStdioKind::Default => { let mut read_from = read_from.expect("read_from must be Some when stdio kind is Default"); let mut buffer = Vec::new(); read_from.read_to_end(&mut buffer).await.into_lua_err()?; buffer } ProcessSpawnOptionsStdioKind::Inherit => { let mut read_from = read_from.expect("read_from must be Some when stdio kind is Inherit"); let mut stdout = io::stdout(); let mut tee = AsyncTeeWriter::new(&mut stdout); io::copy(&mut read_from, &mut tee).await.into_lua_err()?; tee.into_vec() } }) } pub(super) async fn wait_for_child( mut child: Child, stdout_kind: ProcessSpawnOptionsStdioKind, stderr_kind: ProcessSpawnOptionsStdioKind, ) -> LuaResult { let stdout_opt = child.stdout.take(); let stderr_opt = child.stderr.take(); let stdout_task = task::spawn(read_with_stdio_kind(stdout_opt, stdout_kind)); let stderr_task = task::spawn(read_with_stdio_kind(stderr_opt, stderr_kind)); let status = child.wait().await.expect("Child process failed to start"); let stdout_buffer = stdout_task.await.into_lua_err()??; let stderr_buffer = stderr_task.await.into_lua_err()??; Ok(WaitForChildResult { status, stdout: stdout_buffer, stderr: stderr_buffer, }) }