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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { 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"); }