feat: support stdin and pipe for Child API (#1033)

This commit is contained in:
三咲雅 · Misaki Masa 2024-05-14 22:07:19 +08:00 committed by GitHub
parent 07342a29ef
commit 28dfe728ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 77 additions and 26 deletions

View file

@ -70,6 +70,7 @@ impl Input {
let snap = self.snap_mut(); let snap = self.snap_mut();
match opt.kind.as_str() { match opt.kind.as_str() {
"all" => self.kill_range(..),
"bol" => { "bol" => {
let end = snap.idx(snap.cursor).unwrap_or(snap.len()); let end = snap.idx(snap.cursor).unwrap_or(snap.len());
self.kill_range(..end) self.kill_range(..end)

View file

@ -87,16 +87,17 @@ impl Input {
} }
pub(super) fn flush_value(&mut self) { pub(super) fn flush_value(&mut self) {
let Some(tx) = &self.callback else { return };
self.ticket = self.ticket.wrapping_add(1); self.ticket = self.ticket.wrapping_add(1);
if self.realtime { if self.realtime {
let value = self.snap().value.clone(); let value = self.snap().value.clone();
self.callback.as_ref().unwrap().send(Err(InputError::Typed(value))).ok(); tx.send(Err(InputError::Typed(value))).ok();
} }
if self.completion { if self.completion {
let before = self.partition()[0].to_owned(); let before = self.partition()[0].to_owned();
self.callback.as_ref().unwrap().send(Err(InputError::Completed(before, self.ticket))).ok(); tx.send(Err(InputError::Completed(before, self.ticket))).ok();
} }
} }
} }

View file

@ -1,25 +1,25 @@
use std::time::Duration; use std::time::Duration;
use futures::future::try_join3; use futures::future::try_join3;
use mlua::{AnyUserData, IntoLuaMulti, Table, UserData, Value}; use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, Value};
use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select}; use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
use super::Status; use super::Status;
use crate::process::Output; use crate::process::Output;
pub struct Child { pub struct Child {
inner: tokio::process::Child, inner: tokio::process::Child,
_stdin: Option<ChildStdin>, stdin: Option<BufWriter<ChildStdin>>,
stdout: Option<BufReader<ChildStdout>>, stdout: Option<BufReader<ChildStdout>>,
stderr: Option<BufReader<ChildStderr>>, stderr: Option<BufReader<ChildStderr>>,
} }
impl Child { impl Child {
pub fn new(mut inner: tokio::process::Child) -> Self { pub fn new(mut inner: tokio::process::Child) -> Self {
let stdin = inner.stdin.take(); let stdin = inner.stdin.take().map(BufWriter::new);
let stdout = inner.stdout.take().map(BufReader::new); let stdout = inner.stdout.take().map(BufReader::new);
let stderr = inner.stderr.take().map(BufReader::new); let stderr = inner.stderr.take().map(BufReader::new);
Self { inner, _stdin: stdin, stdout, stderr } Self { inner, stdin, stdout, stderr }
} }
} }
@ -69,6 +69,26 @@ impl UserData for Child {
Err(_) => Ok((String::new(), 3u8)), Err(_) => Ok((String::new(), 3u8)),
} }
}); });
methods.add_async_method_mut("write_all", |lua, me, src: mlua::String| async move {
let Some(stdin) = &mut me.stdin else {
return Err("stdin is not piped".into_lua_err());
};
match stdin.write_all(src.as_bytes()).await {
Ok(()) => (true, Value::Nil).into_lua_multi(lua),
Err(e) => (false, e.raw_os_error()).into_lua_multi(lua),
}
});
methods.add_async_method_mut("flush", |lua, me, ()| async move {
let Some(stdin) = &mut me.stdin else {
return Err("stdin is not piped".into_lua_err());
};
match stdin.flush().await {
Ok(()) => (true, Value::Nil).into_lua_multi(lua),
Err(e) => (false, e.raw_os_error()).into_lua_multi(lua),
}
});
methods.add_async_method_mut("wait", |lua, me, ()| async move { methods.add_async_method_mut("wait", |lua, me, ()| async move {
match me.inner.wait().await { match me.inner.wait().await {
Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(lua), Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(lua),
@ -107,5 +127,18 @@ impl UserData for Child {
Ok(_) => (true, Value::Nil).into_lua_multi(lua), Ok(_) => (true, Value::Nil).into_lua_multi(lua),
Err(e) => (false, e.raw_os_error()).into_lua_multi(lua), Err(e) => (false, e.raw_os_error()).into_lua_multi(lua),
}); });
methods.add_method_mut("take_stdin", |lua, me, ()| match me.stdin.take() {
Some(stdin) => lua.create_any_userdata(stdin.into_inner())?.into_lua(lua),
None => Ok(Value::Nil),
});
methods.add_method_mut("take_stdout", |lua, me, ()| match me.stdout.take() {
Some(stdout) => lua.create_any_userdata(stdout.into_inner())?.into_lua(lua),
None => Ok(Value::Nil),
});
methods.add_method_mut("take_stderr", |lua, me, ()| match me.stderr.take() {
Some(stderr) => lua.create_any_userdata(stderr.into_inner())?.into_lua(lua),
None => Ok(Value::Nil),
});
} }
} }

View file

@ -1,6 +1,7 @@
use std::process::Stdio; use std::process::Stdio;
use mlua::{AnyUserData, IntoLuaMulti, Lua, Table, UserData, Value}; use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, Table, UserData, Value};
use tokio::process::{ChildStderr, ChildStdin, ChildStdout};
use super::{output::Output, Child}; use super::{output::Output, Child};
@ -36,6 +37,33 @@ impl Command {
impl UserData for Command { impl UserData for Command {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
#[inline]
fn make_stdio(v: Value) -> mlua::Result<Stdio> {
match v {
Value::Integer(n) => {
return Ok(match n as u8 {
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
}
Value::UserData(ud) => {
if let Ok(stdin) = ud.take::<ChildStdin>() {
return Ok(stdin.try_into()?);
} else if let Ok(stdout) = ud.take::<ChildStdout>() {
return Ok(stdout.try_into()?);
} else if let Ok(stderr) = ud.take::<ChildStderr>() {
return Ok(stderr.try_into()?);
}
}
_ => {}
}
Err(
"must be one of Command.NULL, Command.PIPED, Command.INHERIT, or a ChildStdin, ChildStdout, or ChildStderr".into_lua_err(),
)
}
methods.add_function("arg", |_, (ud, arg): (AnyUserData, mlua::String)| { methods.add_function("arg", |_, (ud, arg): (AnyUserData, mlua::String)| {
ud.borrow_mut::<Self>()?.inner.arg(arg.to_string_lossy().as_ref()); ud.borrow_mut::<Self>()?.inner.arg(arg.to_string_lossy().as_ref());
Ok(ud) Ok(ud)
@ -62,28 +90,16 @@ impl UserData for Command {
Ok(ud) Ok(ud)
}, },
); );
methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, u8)| { methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdin(match stdio { ud.borrow_mut::<Self>()?.inner.stdin(make_stdio(stdio)?);
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
Ok(ud) Ok(ud)
}); });
methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, u8)| { methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdout(match stdio { ud.borrow_mut::<Self>()?.inner.stdout(make_stdio(stdio)?);
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
Ok(ud) Ok(ud)
}); });
methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, u8)| { methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stderr(match stdio { ud.borrow_mut::<Self>()?.inner.stderr(make_stdio(stdio)?);
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
Ok(ud) Ok(ud)
}); });
methods.add_method_mut("spawn", |lua, me, ()| match me.inner.spawn() { methods.add_method_mut("spawn", |lua, me, ()| match me.inner.spawn() {