// https://stackoverflow.com/questions/71141122/- use std::{ io, io::Write, process::{Child, ExitStatus}, }; use mlua::prelude::*; pub struct TeeWriter<'a, W0: Write, W1: Write> { w0: &'a mut W0, w1: &'a mut W1, } impl<'a, W0: Write, W1: Write> TeeWriter<'a, W0, W1> { pub fn new(w0: &'a mut W0, w1: &'a mut W1) -> Self { Self { w0, w1 } } } impl<'a, W0: Write, W1: Write> Write for TeeWriter<'a, W0, W1> { fn write(&mut self, buf: &[u8]) -> io::Result { // We have to use write_all() otherwise what // happens if different amounts are written? self.w0.write_all(buf)?; self.w1.write_all(buf)?; Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { self.w0.flush()?; self.w1.flush()?; Ok(()) } } pub fn pipe_and_inherit_child_process_stdio( mut child: Child, ) -> LuaResult<(ExitStatus, Vec, Vec)> { // https://stackoverflow.com/questions/71141122/- let mut child_stdout = child.stdout.take().unwrap(); let mut child_stderr = child.stderr.take().unwrap(); std::thread::scope(|s| { let stdout_thread = s.spawn(|| { let stdout = io::stdout(); let mut log = Vec::new(); let mut stdout = stdout.lock(); let mut tee = TeeWriter::new(&mut stdout, &mut log); io::copy(&mut child_stdout, &mut tee).map_err(LuaError::external)?; Ok(log) }); let stderr_thread = s.spawn(|| { let stderr = io::stderr(); let mut log = Vec::new(); let mut stderr = stderr.lock(); let mut tee = TeeWriter::new(&mut stderr, &mut log); io::copy(&mut child_stderr, &mut tee).map_err(LuaError::external)?; Ok(log) }); let status = child.wait().expect("child wasn't running"); let stdout_log: Result<_, LuaError> = stdout_thread.join().expect("stdout thread panicked"); let stderr_log: Result<_, LuaError> = stderr_thread.join().expect("stderr thread panicked"); Ok::<_, LuaError>((status, stdout_log?, stderr_log?)) }) }