Skip to content

Commit

Permalink
feat: support stdin and pipe for Child API (#1033)
Browse files Browse the repository at this point in the history
  • Loading branch information
sxyazi authored May 14, 2024
1 parent 07342a2 commit 28dfe72
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 26 deletions.
1 change: 1 addition & 0 deletions yazi-core/src/input/commands/kill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ impl Input {
let snap = self.snap_mut();

match opt.kind.as_str() {
"all" => self.kill_range(..),
"bol" => {
let end = snap.idx(snap.cursor).unwrap_or(snap.len());
self.kill_range(..end)
Expand Down
5 changes: 3 additions & 2 deletions yazi-core/src/input/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,17 @@ impl Input {
}

pub(super) fn flush_value(&mut self) {
let Some(tx) = &self.callback else { return };
self.ticket = self.ticket.wrapping_add(1);

if self.realtime {
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 {
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();
}
}
}
Expand Down
43 changes: 38 additions & 5 deletions yazi-plugin/src/process/child.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
use std::time::Duration;

use futures::future::try_join3;
use mlua::{AnyUserData, IntoLuaMulti, Table, UserData, Value};
use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, Value};
use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select};

use super::Status;
use crate::process::Output;

pub struct Child {
inner: tokio::process::Child,
_stdin: Option<ChildStdin>,
stdin: Option<BufWriter<ChildStdin>>,
stdout: Option<BufReader<ChildStdout>>,
stderr: Option<BufReader<ChildStderr>>,
}

impl Child {
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 stderr = inner.stderr.take().map(BufReader::new);
Self { inner, _stdin: stdin, stdout, stderr }
Self { inner, stdin, stdout, stderr }
}
}

Expand Down Expand Up @@ -69,6 +69,26 @@ impl UserData for Child {
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 {
match me.inner.wait().await {
Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(lua),
Expand Down Expand Up @@ -107,5 +127,18 @@ impl UserData for Child {
Ok(_) => (true, Value::Nil).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),
});
}
}
54 changes: 35 additions & 19 deletions yazi-plugin/src/process/command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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};

Expand Down Expand Up @@ -36,6 +37,33 @@ impl Command {

impl UserData for Command {
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)| {
ud.borrow_mut::<Self>()?.inner.arg(arg.to_string_lossy().as_ref());
Ok(ud)
Expand All @@ -62,28 +90,16 @@ impl UserData for Command {
Ok(ud)
},
);
methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.inner.stdin(match stdio {
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdin(make_stdio(stdio)?);
Ok(ud)
});
methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.inner.stdout(match stdio {
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdout(make_stdio(stdio)?);
Ok(ud)
});
methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.inner.stderr(match stdio {
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
});
methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stderr(make_stdio(stdio)?);
Ok(ud)
});
methods.add_method_mut("spawn", |lua, me, ()| match me.inner.spawn() {
Expand Down

0 comments on commit 28dfe72

Please sign in to comment.