use std::collections::HashMap;
use std::io::Write;
use std::process::Stdio;
use tan::error::Error;
use tan::util::module_util::require_module;
use tan::{context::Context, expr::Expr};
pub fn process_exit(args: &[Expr]) -> Result<Expr, Error> {
std::io::stdout().flush().expect("stdout flushed");
std::io::stderr().flush().expect("stderr flushed");
if let Some(code) = args.first() {
let Some(code) = code.as_int() else {
return Err(Error::invalid_arguments(
"expected Int argument",
code.range(),
));
};
std::process::exit(code as i32);
} else {
std::process::exit(0);
}
}
pub fn process_foreign_args(_args: &[Expr]) -> Result<Expr, Error> {
let mut args = Vec::new();
for arg in std::env::args() {
args.push(Expr::string(arg))
}
Ok(Expr::array(args))
}
pub fn process_args(_args: &[Expr], context: &mut Context) -> Result<Expr, Error> {
let process_args = context.top_scope.get("**process-args**").unwrap();
let process_args = process_args.as_array().unwrap();
let mut args = Vec::new();
for arg in process_args.iter() {
args.push(arg.clone());
}
Ok(Expr::array(args))
}
pub fn process_env_vars(_args: &[Expr]) -> Result<Expr, Error> {
let mut env_vars = HashMap::new();
for (key, value) in std::env::vars() {
env_vars.insert(key, Expr::string(value));
}
Ok(Expr::map(env_vars))
}
pub fn process_exec(args: &[Expr]) -> Result<Expr, Error> {
let [cmd] = args else {
return Err(Error::invalid_arguments(
"`exec` requires `cmd` argument",
None,
));
};
let Expr::String(cmd_string) = cmd.unpack() else {
return Err(Error::invalid_arguments(
"`cmd` argument should be a String", cmd.range(),
));
};
let Ok(output) = std::process::Command::new("sh")
.args(["-c", cmd_string])
.output()
else {
return Err(Error::general("failed to execute cmd `{cmd}`"));
};
Ok(Expr::string(
String::from_utf8(output.stdout).unwrap_or_default(),
))
}
pub fn process_shell(args: &[Expr]) -> Result<Expr, Error> {
let [cmd] = args else {
return Err(Error::invalid_arguments(
"`exec` requires `cmd` argument",
None,
));
};
let Expr::String(cmd_string) = cmd.unpack() else {
return Err(Error::invalid_arguments(
"`cmd` argument should be a String", cmd.range(),
));
};
let Ok(mut process) = std::process::Command::new("sh")
.arg("-c")
.arg(cmd_string)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
else {
return Err(Error::general("failed to spawn cmd `{cmd}`"));
};
let Ok(status) = process.wait() else {
return Err(Error::general("failed to spawn cmd `{cmd}`"));
};
let Some(code) = status.code() else {
return Err(Error::general("process terminated by signal, cmd `{cmd}`"));
};
Ok(Expr::Int(code as i64))
}
pub fn import_lib_process(context: &mut Context) {
let module = require_module("process", context);
module.insert_invocable("exit", Expr::foreign_func(&process_exit));
module.insert_invocable("exit$$", Expr::foreign_func(&process_exit));
module.insert_invocable("args", Expr::foreign_func_mut_context(&process_args));
module.insert_invocable("args$$", Expr::foreign_func_mut_context(&process_args));
module.insert_invocable("env-vars", Expr::foreign_func(&process_env_vars));
module.insert_invocable("env-vars$$", Expr::foreign_func(&process_env_vars));
module.insert_invocable("exec", Expr::foreign_func(&process_exec));
module.insert_invocable("exec$$String", Expr::foreign_func(&process_exec));
module.insert_invocable("shell", Expr::foreign_func(&process_shell));
module.insert_invocable("shell$$String", Expr::foreign_func(&process_shell));
}