diff --git a/src/lune/builtins/process/mod.rs b/src/lune/builtins/process/mod.rs index 212d325..4860b3c 100644 --- a/src/lune/builtins/process/mod.rs +++ b/src/lune/builtins/process/mod.rs @@ -7,8 +7,11 @@ use std::{ use dunce::canonicalize; use mlua::prelude::*; use os_str_bytes::RawOsString; +use tokio::{io::AsyncWriteExt, task}; -use crate::lune::{scheduler::Scheduler, util::TableBuilder}; +use crate::lune::{ + builtins::process::tee_writer::AsyncTeeWriter, scheduler::Scheduler, util::TableBuilder, +}; mod tee_writer; @@ -202,14 +205,32 @@ async fn spawn_command( options: ProcessSpawnOptions, ) -> LuaResult<(ExitStatus, Vec, Vec)> { let inherit_stdio = options.inherit_stdio; + let stdin = options.stdin.clone(); - let child = options + let mut child = options .into_command(program, args) - .stdin(Stdio::null()) + .stdin(match stdin.is_some() { + true => Stdio::piped(), + false => Stdio::null(), + }) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn()?; + // If the stdin option was provided, we write that to the child + if let Some(stdin) = stdin { + let mut child_stdin = child.stdin.take().unwrap(); + + let stdin_writer_thread = task::spawn(async move { + let mut tee = AsyncTeeWriter::new(&mut child_stdin); + tee.write_all(stdin.as_bytes()).await.unwrap(); + }); + + stdin_writer_thread + .await + .expect("Tee writer for stdin errored"); + } + if inherit_stdio { pipe_and_inherit_child_process_stdio(child).await } else { diff --git a/src/lune/builtins/process/options.rs b/src/lune/builtins/process/options.rs index 688caa3..2a2d679 100644 --- a/src/lune/builtins/process/options.rs +++ b/src/lune/builtins/process/options.rs @@ -14,6 +14,7 @@ pub struct ProcessSpawnOptions { pub(crate) envs: HashMap, pub(crate) shell: Option, pub(crate) inherit_stdio: bool, + pub(crate) stdin: Option, } impl<'lua> FromLua<'lua> for ProcessSpawnOptions { @@ -133,6 +134,20 @@ impl<'lua> FromLua<'lua> for ProcessSpawnOptions { } } + /* + If we have stdin contents, we need to pass those to the child process + */ + match value.get("stdin")? { + LuaValue::Nil => this.stdin = None, + LuaValue::String(s) => this.stdin = Some(s.to_string_lossy().to_string()), + value => { + return Err(LuaError::RuntimeError(format!( + "Invalid type for option 'stdin' - expected 'string', got '{}'", + value.type_name() + ))) + } + } + Ok(this) } }