wlambda/stdlib/
process.rs

1use crate::compiler::*;
2use crate::vval::*;
3use std::process::{Command, Child, Stdio};
4use std::rc::Rc;
5use std::cell::RefCell;
6
7#[derive(Clone)]
8struct VChildProcess {
9    child: Rc<RefCell<Child>>,
10    id: u32,
11}
12
13impl VValUserData for VChildProcess {
14    fn s(&self) -> String { format!("$<ChildProcess:pid={}>", self.id) }
15    fn i(&self) -> i64 { self.id as i64 }
16    fn as_any(&mut self) -> &mut dyn std::any::Any { self }
17    fn clone_ud(&self) -> Box<dyn VValUserData> {
18        Box::new(self.clone())
19    }
20}
21
22pub fn add_to_symtable(st: &mut SymbolTable) {
23    st.fun("process:spawn", |env: &mut Env, argc: usize| {
24        let cmd_exe = env.arg(0).deref();
25
26        let mut cmd =
27            cmd_exe.with_s_ref(|s| Command::new(s));
28
29        if argc > 1 {
30            let args = env.arg(1).deref();
31            for a in args.iter() {
32                a.0.with_s_ref(|s| cmd.arg(s));
33            }
34        }
35
36        if env.arg(2).with_s_ref(|s| s == "inherit_out") {
37            cmd.stdin(Stdio::null());
38
39        } else if env.arg(2).with_s_ref(|s| s == "inherit_all") {
40            // nop
41
42        } else {
43            cmd.stdin(Stdio::null());
44            cmd.stdout(Stdio::null());
45            cmd.stderr(Stdio::null());
46        }
47
48        let child =
49            match cmd.spawn() {
50                Ok(child) => child,
51                Err(e) => {
52                    return Ok(env.new_err(
53                        format!("Error executing '{}': {}", cmd_exe.s(), e)));
54                },
55            };
56
57        let id = child.id();
58        let child = Rc::new(RefCell::new(child));
59        Ok(VVal::new_usr(VChildProcess { child, id }))
60    }, Some(1), Some(3), false);
61
62    st.fun("process:try_wait", |env: &mut Env, _argc: usize| {
63        let mut chld = env.arg(0);
64        chld.with_usr_ref(|vts: &mut VChildProcess| {
65            match vts.child.borrow_mut().try_wait() {
66                Ok(Some(st)) => {
67                    let ret = VVal::map();
68                    ret.set_key_str("status", VVal::Int(st.code().unwrap_or(-1) as i64))
69                        .expect("single use");
70                    ret.set_key_str("success", VVal::Bol(st.success()))
71                        .expect("single use");
72                    Ok(ret)
73                },
74                Ok(None) => Ok(VVal::None),
75                Err(e) => {
76                    Ok(env.new_err(
77                        format!("Error try_wait pid={}: {}",
78                            vts.id, e)))
79                }
80            }
81        }).unwrap_or_else(||
82            Ok(env.new_err(format!(
83                "std:process:try_wait: First argument not an IO handle! {}",
84                chld.s()))))
85    }, Some(1), Some(1), false);
86
87    st.fun("process:wait", |env: &mut Env, _argc: usize| {
88        let mut chld = env.arg(0);
89        chld.with_usr_ref(|vts: &mut VChildProcess| {
90            match vts.child.borrow_mut().wait() {
91                Ok(st) => {
92                    let ret = VVal::map();
93                    ret.set_key_str("status", VVal::Int(st.code().unwrap_or(-1) as i64))
94                        .expect("single use");
95                    ret.set_key_str("success", VVal::Bol(st.success()))
96                        .expect("single use");
97                    Ok(ret)
98                },
99                Err(e) => {
100                    Ok(env.new_err(
101                        format!("Error wait pid={}: {}",
102                            vts.id, e)))
103                }
104            }
105        }).unwrap_or_else(||
106            Ok(env.new_err(format!(
107                "std:process:wait: First argument not an IO handle! {}",
108                chld.s()))))
109    }, Some(1), Some(1), false);
110
111    st.fun("process:kill_wait", |env: &mut Env, _argc: usize| {
112        let mut chld = env.arg(0);
113        chld.with_usr_ref(|vts: &mut VChildProcess| {
114            let kill_res = vts.child.borrow_mut().kill();
115            match kill_res {
116                Ok(_) => {
117                    match vts.child.borrow_mut().wait() {
118                        Ok(st) => {
119                            let ret = VVal::map();
120                            ret.set_key_str("status", VVal::Int(st.code().unwrap_or(-1) as i64))
121                                .expect("single use");
122                            ret.set_key_str("success", VVal::Bol(st.success()))
123                                .expect("single use");
124                            Ok(ret)
125                        },
126                        Err(e) => {
127                            Ok(env.new_err(
128                                format!("Error killing & wait pid={}: {}",
129                                    vts.id, e)))
130                        }
131                    }
132                },
133                Err(e) =>
134                    Ok(env.new_err(
135                        format!("Error killing pid={}: {}",
136                            vts.id, e))),
137            }
138        }).unwrap_or_else(||
139            Ok(env.new_err(format!(
140                "std:process:kill_wait: First argument not an IO handle! {}",
141                chld.s()))))
142    }, Some(1), Some(1), false);
143
144    st.fun("process:run", |env: &mut Env, argc: usize| {
145        let cmd_exe = env.arg(0).deref();
146
147        let mut cmd =
148            cmd_exe.with_s_ref(|s| Command::new(s));
149
150        if argc > 1 {
151            let args = env.arg(1).deref();
152            for a in args.iter() {
153                a.0.with_s_ref(|s| cmd.arg(s));
154            }
155        }
156
157        cmd.stdin(Stdio::null());
158
159        match cmd.output() {
160            Ok(out) => {
161                let ret = VVal::map();
162                ret.set_key_str("status", VVal::Int(out.status.code().unwrap_or(-1) as i64))
163                    .expect("single use");
164                ret.set_key_str("success", VVal::Bol(out.status.success()))
165                    .expect("single use");
166                ret.set_key_str("stdout", VVal::new_byt(out.stdout))
167                    .expect("single use");
168                ret.set_key_str("stderr", VVal::new_byt(out.stderr))
169                    .expect("single use");
170
171                Ok(ret)
172            },
173            Err(e) => {
174                return Ok(env.new_err(
175                    format!("Error executing '{}': {}", cmd_exe.s(), e)));
176            },
177        }
178
179    }, Some(1), Some(2), false);
180}