feat: improve Command implementation and error handling

- Replace raw_set with set for safer global table manipulation
- Add proper error handling for UserData type matching in make_stdio
- Use to_str() instead of to_string_lossy() for proper UTF-8 validation
- Improve code organization and readability
- Add missing lua context parameter in command constructor
- Remove redundant braces in args method

These changes make the Command implementation more robust and safer to use while maintaining the existing functionality. The improved error handling will help catch potential issues earlier in the development process.
This commit is contained in:
mnv 2024-10-26 13:37:41 +05:30 committed by sxyazi
parent ef1a31a274
commit abf5af727f
No known key found for this signature in database

View file

@ -1,13 +1,11 @@
use std::process::Stdio;
use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, Table, UserData, Value};
use tokio::process::{ChildStderr, ChildStdin, ChildStdout};
use super::{Child, output::Output};
use crate::process::Status;
pub struct Command {
inner: tokio::process::Command,
inner: tokio::process::Command,
}
const NULL: u8 = 0;
@ -15,109 +13,115 @@ const PIPED: u8 = 1;
const INHERIT: u8 = 2;
impl Command {
pub fn install(lua: &Lua) -> mlua::Result<()> {
let new = lua.create_function(|_, (_, program): (Table, String)| {
let mut inner = tokio::process::Command::new(program);
inner.kill_on_drop(true).stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
pub fn install(lua: &Lua) -> mlua::Result<()> {
let new = lua.create_function(|lua, (table, program): (Table, String)| {
let mut inner = tokio::process::Command::new(program);
inner.kill_on_drop(true)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
Ok(Command { inner })
})?;
Ok(Self { inner })
})?;
let command = lua.create_table_from([
("NULL", NULL),
("PIPED", PIPED),
("INHERIT", INHERIT),
])?;
let command = lua.create_table_from([
// Stdio
("NULL", NULL),
("PIPED", PIPED),
("INHERIT", INHERIT),
])?;
command.set_metatable(Some(lua.create_table_from([("__call", new)])?));
lua.globals().raw_set("Command", command)
}
command.set_metatable(Some(lua.create_table_from([("__call", new)])?));
lua.globals().set("Command", 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()?);
}
}
_ => {}
}
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
fn make_stdio(v: Value) -> mlua::Result<Stdio> {
match v {
Value::Integer(n) => {
Ok(match n as u8 {
PIPED => Stdio::piped(),
INHERIT => Stdio::inherit(),
_ => Stdio::null(),
})
}
Value::UserData(ud) => {
if let Ok(stdin) = ud.take::<ChildStdin>() {
Ok(stdin.try_into()?)
} else if let Ok(stdout) = ud.take::<ChildStdout>() {
Ok(stdout.try_into()?)
} else if let Ok(stderr) = ud.take::<ChildStderr>() {
Ok(stderr.try_into()?)
} else {
Err("Invalid userdata type".into_lua_err())
}
}
_ => Err(
"must be one of Command.NULL, Command.PIPED, Command.INHERIT, or a ChildStdin, ChildStdout, or ChildStderr".into_lua_err(),
)
}
}
Err(
"must be one of Command.NULL, Command.PIPED, Command.INHERIT, or a ChildStdin, ChildStdout, or ChildStderr".into_lua_err(),
)
}
methods.add_function_mut("arg", |_, (ud, arg): (AnyUserData, mlua::String)| {
ud.borrow_mut::<Self>()?.inner.arg(arg.to_str()?);
Ok(ud)
});
methods.add_function_mut("arg", |_, (ud, arg): (AnyUserData, mlua::String)| {
ud.borrow_mut::<Self>()?.inner.arg(arg.to_string_lossy().as_ref());
Ok(ud)
});
methods.add_function_mut("args", |_, (ud, args): (AnyUserData, Vec<mlua::String>)| {
{
let mut me = ud.borrow_mut::<Self>()?;
for arg in args {
me.inner.arg(arg.to_string_lossy().as_ref());
}
}
Ok(ud)
});
methods.add_function_mut("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| {
ud.borrow_mut::<Self>()?.inner.current_dir(dir.to_str()?);
Ok(ud)
});
methods.add_function_mut(
"env",
|_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| {
ud.borrow_mut::<Self>()?
.inner
.env(key.to_string_lossy().as_ref(), value.to_string_lossy().as_ref());
Ok(ud)
},
);
methods.add_function_mut("stdin", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdin(make_stdio(stdio)?);
Ok(ud)
});
methods.add_function_mut("stdout", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdout(make_stdio(stdio)?);
Ok(ud)
});
methods.add_function_mut("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() {
Ok(child) => (Child::new(child), Value::Nil).into_lua_multi(lua),
Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
});
methods.add_async_method_mut("output", |lua, me, ()| async move {
match me.inner.output().await {
Ok(output) => (Output::new(output), Value::Nil).into_lua_multi(lua),
Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
}
});
methods.add_async_method_mut("status", |lua, me, ()| async move {
match me.inner.status().await {
Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(lua),
Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
}
});
}
methods.add_function_mut("args", |_, (ud, args): (AnyUserData, Vec<mlua::String>)| {
let mut me = ud.borrow_mut::<Self>()?;
for arg in args {
me.inner.arg(arg.to_str()?);
}
Ok(ud)
});
methods.add_function_mut("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| {
ud.borrow_mut::<Self>()?.inner.current_dir(dir.to_str()?);
Ok(ud)
});
methods.add_function_mut(
"env",
|_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| {
ud.borrow_mut::<Self>()?
.inner
.env(key.to_str()?, value.to_str()?);
Ok(ud)
},
);
methods.add_function_mut("stdin", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdin(make_stdio(stdio)?);
Ok(ud)
});
methods.add_function_mut("stdout", |_, (ud, stdio): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.inner.stdout(make_stdio(stdio)?);
Ok(ud)
});
methods.add_function_mut("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() {
Ok(child) => (Child::new(child), Value::Nil).into_lua_multi(lua),
Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
});
methods.add_async_method_mut("output", |lua, me, ()| async move {
match me.inner.output().await {
Ok(output) => (Output::new(output), Value::Nil).into_lua_multi(lua),
Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
}
});
methods.add_async_method_mut("status", |lua, me, ()| async move {
match me.inner.status().await {
Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(lua),
Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
}
});
}
}