use rlua::prelude::*;use std::{    mem,    collections::HashMap,    io::{BufReader, prelude::*},    process::{        Command,        Child,        ChildStdout,        ChildStdin,        ChildStderr,        ExitStatus,        Stdio,        Output    }};use crate::error::Error;pub struct LuaCommand(Command);pub struct LuaChild {    child: Child,    stdin: ChildStdin,    stdout: BufReader<ChildStdout>,    stderr: BufReader<ChildStderr>}pub struct LuaOutput(Output);pub struct LuaExitStatus(ExitStatus);impl LuaUserData for LuaCommand {    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {        fn stdio_type(stdtype: Option<&String>) -> Stdio {            match stdtype.map(|s| s.as_str()) {                Some("inherit") => Stdio::inherit(),                Some("null") => Stdio::null(),                Some("piped") | _ => Stdio::piped(),            }        }        methods.add_method_mut("arg", |_, this: &mut LuaCommand, arg: String|{            this.0.arg(arg);            Ok(())        });        methods.add_method_mut("args", |_, this: &mut LuaCommand, args: Vec<String>|{            this.0.args(args);            Ok(())        });        methods.add_method_mut("env", |_, this: &mut LuaCommand, (k, v): (String, String)|{            this.0.env(k, v);            Ok(())        });        methods.add_method_mut("envs", |_, this: &mut LuaCommand, env: HashMap<String, String>|{            this.0.envs(env);            Ok(())        });        methods.add_method_mut("env_clear", |_, this: &mut LuaCommand, key: Option<String>|{            match key {                Some(key) => this.0.env_remove(key),                None => this.0.env_clear()            };            Ok(())        });        methods.add_method_mut("directory", |_, this: &mut LuaCommand, dir: String|{            this.0.current_dir(dir);            Ok(())        });        methods.add_method_mut("spawn", |_, this: &mut LuaCommand, args: Option<HashMap<String, String>>|{            if let Some(args) = args {                this.0.stdin(stdio_type(args.get("stdin")))                    .stdout(stdio_type(args.get("stdout")))                    .stderr(stdio_type(args.get("stderr")));            } else {                this.0.stdin(Stdio::piped())                    .stdout(Stdio::piped())                    .stderr(Stdio::piped());            }            let mut child = this.0.spawn().map_err(LuaError::external)?;            let stdout = mem::replace(&mut child.stdout, None).map(BufReader::new).ok_or(LuaError::external(Error::InternalError))?;            let stderr = mem::replace(&mut child.stderr, None).map(BufReader::new).ok_or(LuaError::external(Error::InternalError))?;            let stdin = mem::replace(&mut child.stdin, None).ok_or(LuaError::external(Error::InternalError))?;                        Ok(LuaChild {                child: child,                stdin: stdin,                stdout: stdout,                stderr: stderr            })        });        methods.add_method_mut("exec", |_, this: &mut LuaCommand, _: ()|{            this.0.output().map(LuaOutput).map_err(LuaError::external)        });    }}//TODO: Have stdout and stderr share a common binding. Maybe by splitting the method into stdin, and have stdout/stderr share the same interface since they both implement `Read` traitimpl LuaUserData for LuaChild {    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {        methods.add_method_mut("kill", |_, this: &mut LuaChild, _: ()|{            this.child.kill().map_err(LuaError::external)        });        methods.add_method_mut("id", |_, this: &mut LuaChild, _: ()|{            Ok(this.child.id())        });        methods.add_method_mut("wait", |_, this: &mut LuaChild, _: ()|{            this.child.wait().map(LuaExitStatus).map_err(LuaError::external)        });        methods.add_method_mut("write", |_, this: &mut LuaChild, bytes: Vec<u8>|{            this.stdin.write(bytes.as_slice()).map_err(LuaError::external)        });        methods.add_method_mut("write", |_, this: &mut LuaChild, data: String|{            this.stdin.write(data.as_bytes()).map_err(LuaError::external)        });        methods.add_method_mut("flush", |_, this: &mut LuaChild, _: ()| {            this.stdin.flush().map_err(LuaError::external)        });        methods.add_method_mut("read", |_, this: &mut LuaChild, len: Option<usize>|{            let bytes = match len {                Some(len) => {                    let mut bytes = vec![0u8; len];                    this.stdout.read(&mut bytes).map_err(LuaError::external)?;                    bytes                },                None => {                    let mut bytes = vec![];                    this.stdout.read_to_end(&mut bytes).map_err(LuaError::external)?;                    bytes                }            };            Ok(bytes)        });        methods.add_method_mut("read_to_string", |_, this: &mut LuaChild, _:()|{            let mut data = String::new();            this.stdout.read_to_string(&mut data).map_err(LuaError::external)?;            Ok(data)        });        methods.add_method_mut("read_line", |_, this: &mut LuaChild, _: ()|{            let mut data = String::new();            this.stdout.read_line(&mut data).map_err(LuaError::external)?;            Ok(data)        });        methods.add_method_mut("read_error", |_, this: &mut LuaChild, len: Option<usize>|{            let bytes = match len {                Some(len) => {                    let mut bytes = vec![0u8; len];                    this.stderr.read(&mut bytes).map_err(LuaError::external)?;                    bytes                },                None => {                    let mut bytes = vec![];                    this.stderr.read_to_end(&mut bytes).map_err(LuaError::external)?;                    bytes                }            };            Ok(bytes)        });        methods.add_method_mut("read_error_to_string", |_, this: &mut LuaChild, _:()|{            let mut data = String::new();            this.stderr.read_to_string(&mut data).map_err(LuaError::external)?;            Ok(data)        });        methods.add_method_mut("read_error_line", |_, this: &mut LuaChild, _: ()|{            let mut data = String::new();            this.stderr.read_line(&mut data).map_err(LuaError::external)?;            Ok(data)        });    }}impl LuaUserData for LuaOutput {    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {        methods.add_method("status", |_, this: &LuaOutput, _: ()|{            Ok(LuaExitStatus(this.0.status))        });        methods.add_method_mut("stdout", |_, this: &mut LuaOutput, _: ()|{            String::from_utf8(this.0.stdout.clone()).map_err(LuaError::external)        });        methods.add_method_mut("stderr", |_, this: &mut LuaOutput, _: ()|{            String::from_utf8(this.0.stderr.clone()).map_err(LuaError::external)        });    }}impl LuaUserData for LuaExitStatus {    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {        methods.add_method("success", |_, this: &LuaExitStatus, _: ()|{            Ok(this.0.success())        });        methods.add_method("code", |_, this: &LuaExitStatus, _: ()|{            Ok(this.0.code())        });    }}#[allow(unreachable_code)]pub fn init(lua: &Lua) -> crate::Result<()> {    let module = lua.create_table()?;    module.set("new", lua.create_function( |_, (name, args): (String, Option<Vec<String>>)| {        let mut command = Command::new(name);        if let Some(args) = args {            command.args(args);        }        Ok(LuaCommand(command))    })? )?;    lua.globals().set("command", module)?;    Ok(())}