summaryrefslogtreecommitdiff
path: root/matrix-stdlib/src/sys.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--matrix-stdlib/src/sys.rs99
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");
+}