mtots_core 0.1.2

Core implementation of the mtots scripting language
Documentation
use crate::ArgSpec;
use crate::Encoding;
use crate::Error;
use crate::NativeModule;
use crate::RcStr;
use crate::Result;
use crate::Value;
use std::convert::TryFrom;
use std::process as pr;

const NAME: &'static str = "a.proc";

pub(super) fn new() -> NativeModule {
    NativeModule::new(NAME, |m| {
        m.doc("Utilities for spawning and interacting with subprocesses");

        m.func(
            "run",
            ArgSpec::builder()
                .req("cmd")
                .def("args", [])
                .def("stdin", "inherit")
                .def("stdout", "inherit")
                .def("stderr", "inherit")
                .def("dir", ())
                .def("encoding", ())
                .def("clear_envs", false)
                .def("envs", ()),
            "",
            |globals, args, _| {
                let mut args = args.into_iter();
                let cmd = args.next().unwrap().into_string()?;
                let mut cmd = pr::Command::new(cmd);
                cmd.args(Vec::<RcStr>::try_from(args.next().unwrap())?);
                cmd.stdin(pr::Stdio::try_from(args.next().unwrap())?);
                cmd.stdout(pr::Stdio::try_from(args.next().unwrap())?);
                cmd.stderr(pr::Stdio::try_from(args.next().unwrap())?);
                match args.next().unwrap() {
                    Value::Nil => {}
                    value => {
                        let dir = value.into_string()?;
                        cmd.current_dir(dir.str());
                    }
                };
                let encoding = Encoding::try_from(args.next().unwrap())?;
                let clear_envs = args.next().unwrap().truthy();
                if clear_envs {
                    cmd.env_clear();
                }
                let envs = args.next().unwrap();
                if !envs.is_nil() {
                    let envs: Vec<(RcStr, RcStr)> = envs.unpack_into(globals)?;
                    cmd.envs(envs);
                }
                let child = cmd.spawn()?;
                let output = child.wait_with_output()?;
                let code = output.status.code().map(Value::from).unwrap_or(Value::Nil);
                let stdout = encoding.decode(globals, output.stdout)?;
                let stderr = encoding.decode(globals, output.stderr)?;
                Ok(Value::from(vec![code, stdout, stderr]))
            },
        );
    })
}

impl TryFrom<Value> for pr::Stdio {
    type Error = Error;
    fn try_from(value: Value) -> Result<pr::Stdio> {
        let opt = match &value {
            Value::String(string) => match string.str() {
                "inherit" => Some(pr::Stdio::inherit()),
                "pipe" => Some(pr::Stdio::piped()),
                "null" => Some(pr::Stdio::null()),
                _ => None,
            },
            _ => None,
        };
        match opt {
            Some(opt) => Ok(opt),
            None => Err(rterr!(
                "Expected 'inherit', 'pipe' or 'null' but got {:?}",
                value
            )),
        }
    }
}