diff options
Diffstat (limited to '')
-rw-r--r-- | matrix-stdlib/src/sys.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/matrix-stdlib/src/sys.rs b/matrix-stdlib/src/sys.rs new file mode 100644 index 0000000..e91e635 --- /dev/null +++ b/matrix-stdlib/src/sys.rs @@ -0,0 +1,99 @@ +use std::{process::{exit, Command, Stdio}, env, rc::Rc, io::Read}; + +use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc}; +use matrix_macros::native_func; +use crate::{VmArgs, error}; + +#[native_func(1)] +fn sys_exit(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::Int(i) = value else { + return error!("exit requires a int exit code") + }; + exit(i as i32); +} + +#[native_func(0)] +fn argv(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::List( + Gc::new( + env::args() + .map(|a| Value::String(Rc::from(a.as_str()))) + .collect() + ))) +} + +#[native_func(1)] +fn env(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("env requires a string name") + }; + match std::env::var(value.as_ref()) { + Ok(v) => Ok(Value::String(v.into())), + Err(e) => error!("couldn't read env var: {e}") + } +} + +#[native_func(2)] +fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd, args] = unpack_args!(args); + let (cmd, args) = match (cmd, args) { + (Value::String(s), Value::List(l)) => (s, l.into_inner()), + _ => return error!("exec requires a string cmd and string argument list") + }; + let mut sargs = Vec::new(); + for arg in args { + let Value::String(arg) = arg else { + return error!("exec requires a string cmd and string argument list") + }; + sargs.push(arg.to_string()); + }; + let cmd = Command::new(cmd.to_string()) + .args(sargs) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + let mut child = match cmd { + Ok(c) => c, + Err(e) => return error!("error executing command: {e}") + }; + let status = match child.wait() { + Ok(s) => s, + Err(e) => return error!("error executing command: {e}") + }; + + let stdout = match child.stdout { + Some(ref mut out) => { + let mut buf = String::new(); + let _ = out.read_to_string(&mut buf); + buf + }, + None => String::new() + }; + + let stderr = match child.stderr { + Some(ref mut err) => { + let mut buf = String::new(); + let _ = err.read_to_string(&mut buf); + buf + }, + None => String::new() + }; + + let mut res = ValueMap::new(); + res.insert(Value::from("success"), Value::Bool(status.success()))?; + res.insert(Value::from("code"), Value::Int(status.code().unwrap_or(0) as i64))?; + res.insert(Value::from("out"), Value::String(stdout.into()))?; + res.insert(Value::from("err"), Value::String(stderr.into()))?; + + Ok(Value::Table(res.into())) +} + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(sys_exit(), "exit"); + vm.load_global_fn(argv(), "argv"); + vm.load_global_fn(exec(), "exec"); + vm.load_global_fn(env(), "env"); +} |