rust_process_interface_library/
lib.rs

1//! How to use this crate
2//! # Adding this as a dependency
3//! ```rust, ignore
4//! [dependencies]
5//! rust_process_interface_library = "^0.1"
6//! ```
7//!
8//! # Bringing this into scope
9//! ```rust, ignore
10//! use rust_process_interface_library::Command;
11//! ```
12//! # Tests
13//! ```bash, ignore
14//! cargo test --lib
15//! ```
16
17use std::collections::HashMap;
18use std::env;
19use std::ffi::CString;
20
21/// The output of a finished process.
22///
23/// This is returned in a Result by the [`output`] method of a [`Command`].
24pub struct Output {
25    /// The status (exit code) of the process.
26    pub status: i32,
27    /// The data that the process wrote to stdout.
28    pub stdout: Vec<u8>,
29    /// The data that the process wrote to stderr.
30    pub stderr: Vec<u8>,
31}
32
33pub mod ssvm_process {
34    use std::os::raw::c_char;
35    #[link(wasm_import_module = "ssvm_process")]
36    extern "C" {
37        pub fn ssvm_process_set_prog_name(name: *const c_char, len: u32);
38        pub fn ssvm_process_add_arg(arg: *const c_char, len: u32);
39        pub fn ssvm_process_add_env(
40            env: *const c_char,
41            env_len: u32,
42            val: *const c_char,
43            val_len: u32,
44        );
45        pub fn ssvm_process_add_stdin(buf: *const c_char, len: u32);
46        pub fn ssvm_process_set_timeout(time_ms: u32);
47        pub fn ssvm_process_run() -> i32;
48        pub fn ssvm_process_get_exit_code() -> i32;
49        pub fn ssvm_process_get_stdout_len() -> u32;
50        pub fn ssvm_process_get_stdout(buf: *mut u8);
51        pub fn ssvm_process_get_stderr_len() -> u32;
52        pub fn ssvm_process_get_stderr(buf: *mut u8);
53    }
54}
55
56pub struct Command {
57    /// The program name.
58    pub name: String,
59    /// The argument list.
60    pub args_list: Vec<String>,
61    /// The environment map.
62    pub envp_map: HashMap<String, String>,
63    /// The timeout value (milliseconds).
64    pub timeout_val: u32,
65    /// Buffered stdin.
66    pub stdin_str: Vec<u8>,
67}
68
69impl Command {
70    pub fn new<S: AsRef<str>>(prog: S) -> Command {
71        let mut envp: HashMap<String, String> = HashMap::new();
72        for (key, value) in env::vars() {
73            envp.insert(key, value);
74        }
75        Command {
76            name: String::from(prog.as_ref()),
77            args_list: vec![],
78            envp_map: envp,
79            timeout_val: 10000,
80            stdin_str: vec![],
81        }
82    }
83
84    pub fn arg<S: AsRef<str>>(&mut self, arg: S) -> &mut Command {
85        self.args_list.push(String::from(arg.as_ref()));
86        self
87    }
88
89    pub fn args<I, S>(&mut self, args: I) -> &mut Command
90    where
91        I: IntoIterator<Item = S>,
92        S: AsRef<str>,
93    {
94        for arg in args {
95            self.arg(arg.as_ref());
96        }
97        self
98    }
99
100    pub fn args_clear(&mut self) -> &mut Command {
101        self.args_list.clear();
102        self
103    }
104
105    pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
106    where
107        K: AsRef<str>,
108        V: AsRef<str>,
109    {
110        self.envp_map
111            .insert(String::from(key.as_ref()), String::from(val.as_ref()));
112        self
113    }
114
115    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
116    where
117        I: IntoIterator<Item = (K, V)>,
118        K: AsRef<str>,
119        V: AsRef<str>,
120    {
121        for (ref key, ref val) in vars {
122            self.env(key.as_ref(), val.as_ref());
123        }
124        self
125    }
126
127    pub fn stdin<S: AsRef<str>>(&mut self, buf: S) -> &mut Command {
128        self.stdin_str
129            .extend(CString::new(buf.as_ref()).expect("").as_bytes());
130        self
131    }
132
133    pub fn stdin_u8(&mut self, buf: u8) -> &mut Command {
134        self.stdin_str.push(buf);
135        self
136    }
137
138    pub fn stdin_u8vec<S: AsRef<[u8]>>(&mut self, buf: S) -> &mut Command {
139        self.stdin_str.extend(buf.as_ref());
140        self
141    }
142
143    pub fn timeout(&mut self, time: u32) -> &mut Command {
144        self.timeout_val = time;
145        self
146    }
147
148    pub fn output(&mut self) -> Output {
149        unsafe {
150            // Set program name.
151            let cprog = CString::new((&self.name).as_bytes()).expect("");
152            ssvm_process::ssvm_process_set_prog_name(cprog.as_ptr(), cprog.as_bytes().len() as u32);
153
154            // Set arguments.
155            for arg in &self.args_list {
156                let carg = CString::new(arg.as_bytes()).expect("");
157                ssvm_process::ssvm_process_add_arg(carg.as_ptr(), carg.as_bytes().len() as u32);
158            }
159
160            // Set environments.
161            for (key, val) in &self.envp_map {
162                let ckey = CString::new(key.as_bytes()).expect("");
163                let cval = CString::new(val.as_bytes()).expect("");
164                ssvm_process::ssvm_process_add_env(
165                    ckey.as_ptr(),
166                    ckey.as_bytes().len() as u32,
167                    cval.as_ptr(),
168                    cval.as_bytes().len() as u32,
169                );
170            }
171
172            // Set timeout.
173            ssvm_process::ssvm_process_set_timeout(self.timeout_val);
174
175            // Set stdin.
176            ssvm_process::ssvm_process_add_stdin(
177                self.stdin_str.as_ptr() as *const i8,
178                self.stdin_str.len() as u32,
179            );
180
181            // Run.
182            let exit_code = ssvm_process::ssvm_process_run();
183
184            // Get outputs.
185            let stdout_len = ssvm_process::ssvm_process_get_stdout_len();
186            let stderr_len = ssvm_process::ssvm_process_get_stderr_len();
187            let mut stdout_vec: Vec<u8> = vec![0; stdout_len as usize];
188            let mut stderr_vec: Vec<u8> = vec![0; stderr_len as usize];
189            let stdout_ptr = stdout_vec.as_mut_ptr();
190            let stderr_ptr = stderr_vec.as_mut_ptr();
191            ssvm_process::ssvm_process_get_stdout(stdout_ptr);
192            ssvm_process::ssvm_process_get_stderr(stderr_ptr);
193
194            Output {
195                status: exit_code,
196                stdout: stdout_vec,
197                stderr: stderr_vec,
198            }
199        }
200    }
201}
202
203// Test
204// Please use the following command so that the print statements are shown during testing
205// cargo test -- --nocapture
206//
207
208#[cfg(test)]
209mod tests {
210    use super::Command;
211    #[test]
212    fn test_arg() {
213        let mut cmd = Command::new("rusttest");
214        cmd.arg("val1").arg("val2");
215        assert_eq!(cmd.args_list[0], "val1");
216        assert_eq!(cmd.args_list[1], "val2");
217    }
218    #[test]
219    fn test_args() {
220        let mut cmd = Command::new("rusttest");
221        cmd.args(&["val1", "val2"]);
222        assert_eq!(cmd.args_list[0], "val1");
223        assert_eq!(cmd.args_list[1], "val2");
224    }
225    #[test]
226    fn test_arg_args() {
227        let mut cmd = Command::new("rusttest");
228        cmd.arg("val1").arg("val2").args(&["val3", "val4"]);
229        assert_eq!(cmd.args_list[0], "val1");
230        assert_eq!(cmd.args_list[1], "val2");
231        assert_eq!(cmd.args_list[2], "val3");
232        assert_eq!(cmd.args_list[3], "val4");
233    }
234    #[test]
235    fn test_args_clear() {
236        let mut cmd = Command::new("rusttest");
237        cmd.arg("val1").arg("val2").args(&["val3", "val4"]);
238        cmd.args_clear();
239        assert_eq!(cmd.args_list.len(), 0);
240    }
241    #[test]
242    fn test_env() {
243        let mut cmd = Command::new("rusttest");
244        cmd.env("ENV1", "VALUE1").env("ENV2", "VALUE2");
245        assert_eq!(cmd.envp_map["ENV1"], "VALUE1");
246        assert_eq!(cmd.envp_map["ENV2"], "VALUE2");
247    }
248    #[test]
249    fn test_envs() {
250        use std::collections::HashMap;
251        let mut cmd = Command::new("rusttest");
252        let mut hash: HashMap<String, String> = HashMap::new();
253        hash.insert(String::from("ENV1"), String::from("VALUE1"));
254        hash.insert(String::from("ENV2"), String::from("VALUE2"));
255        cmd.envs(hash);
256        assert_eq!(cmd.envp_map["ENV1"], "VALUE1");
257        assert_eq!(cmd.envp_map["ENV2"], "VALUE2");
258    }
259    #[test]
260    fn test_env_envs() {
261        use std::collections::HashMap;
262        let mut cmd = Command::new("rusttest");
263        let mut hash: HashMap<String, String> = HashMap::new();
264        hash.insert(String::from("ENV1"), String::from("VALUE1"));
265        hash.insert(String::from("ENV2"), String::from("VALUE2"));
266        cmd.env("ENV3", "VALUE3").env("ENV4", "VALUE4").envs(hash);
267        assert_eq!(cmd.envp_map["ENV1"], "VALUE1");
268        assert_eq!(cmd.envp_map["ENV2"], "VALUE2");
269        assert_eq!(cmd.envp_map["ENV3"], "VALUE3");
270        assert_eq!(cmd.envp_map["ENV4"], "VALUE4");
271    }
272    #[test]
273    fn test_stdin() {
274        use std::str;
275        let mut cmd = Command::new("rusttest");
276        cmd.stdin("hello").stdin(" ").stdin("world");
277        assert_eq!(
278            str::from_utf8(&cmd.stdin_str).expect("ERROR"),
279            "hello world"
280        );
281    }
282    #[test]
283    fn test_stdin_u8() {
284        let mut cmd = Command::new("rusttest");
285        cmd.stdin("Test").stdin_u8(0).stdin_u8(100).stdin_u8(255);
286        assert_eq!(cmd.stdin_str, vec![84, 101, 115, 116, 0, 100, 255]);
287    }
288    #[test]
289    fn test_stdin_u8vec() {
290        let mut cmd = Command::new("rusttest");
291        let v = vec![5, 6, 7];
292        cmd.stdin("Test")
293            .stdin_u8vec(&v)
294            .stdin_u8(100)
295            .stdin_u8(255);
296        assert_eq!(cmd.stdin_str, vec![84, 101, 115, 116, 5, 6, 7, 100, 255]);
297        assert_eq!(v, vec![5, 6, 7]);
298    }
299    #[test]
300    fn test_timeout() {
301        let mut cmd = Command::new("rusttest");
302        cmd.timeout(666666);
303        assert_eq!(cmd.timeout_val, 666666);
304    }
305}