exec_target/
lib.rs

1/*!
2the simple invoke command for test
3
4This invokes external a command and manipulates standard in out.
5You can use `std::process::Command` more easily.
6
7# Features
8
9- minimum support rustc 1.56.1 (59eed8a2a 2021-11-01)
10
11# Example
12
13```rust
14use exec_target::exec_target_with_env_in;
15
16let command = "target/debug/exe-stab-grep";
17let args = &["--color=always", "-e", "c"];
18let envs = vec![("GREP_COLORS", "ms=01;32")];
19let inp = b"abcdefg\n" as &[u8];
20
21let oup = exec_target_with_env_in(command, args, envs, inp);
22
23assert_eq!(oup.stderr, "");
24assert_eq!(oup.stdout, "ab\u{1b}[01;32m\u{1b}[Kc\u{1b}[m\u{1b}[Kdefg\n");
25assert_eq!(oup.status.success(), true);
26```
27*/
28use std::collections::HashMap;
29use std::env;
30use std::ffi::OsStr;
31use std::process::{Command, ExitStatus, Output, Stdio};
32
33// trats
34use std::io::Write;
35use std::iter::IntoIterator;
36
37//
38pub struct OutputString {
39    pub status: ExitStatus,
40    pub stdout: String,
41    pub stderr: String,
42}
43
44fn setup_envs<I, K, V>(cmd: &mut Command, vars: I) -> &mut Command
45where
46    I: IntoIterator<Item = (K, V)>,
47    K: AsRef<OsStr>,
48    V: AsRef<OsStr>,
49{
50    let filtered_env: HashMap<String, String> = env::vars()
51        .filter(|(k, _)| k == "TERM" || k == "TZ" || k == "PATH" || k == "LD_LIBRARY_PATH")
52        .collect();
53    cmd.env_clear()
54        .envs(filtered_env)
55        .envs(vars)
56        .env("LANG", "C")
57}
58
59pub fn exec_target<I, S>(target_exe: &str, args: I) -> OutputString
60where
61    I: IntoIterator<Item = S>,
62    S: AsRef<OsStr>,
63{
64    let mut cmd: Command = Command::new(target_exe);
65    setup_envs(&mut cmd, Vec::<(&str, &str)>::new())
66        .args(args)
67        .stdout(Stdio::piped())
68        .stderr(Stdio::piped());
69    let child = cmd.spawn().expect("failed to execute child");
70    let output: Output = child.wait_with_output().expect("failed to wait on child");
71    //
72    OutputString {
73        status: output.status,
74        stdout: String::from(String::from_utf8_lossy(&output.stdout)),
75        stderr: String::from(String::from_utf8_lossy(&output.stderr)),
76    }
77}
78
79pub fn exec_target_with_env<I, S, IKV, K, V>(target_exe: &str, args: I, env: IKV) -> OutputString
80where
81    I: IntoIterator<Item = S>,
82    S: AsRef<OsStr>,
83    IKV: IntoIterator<Item = (K, V)>,
84    K: AsRef<OsStr>,
85    V: AsRef<OsStr>,
86{
87    let mut cmd: Command = Command::new(target_exe);
88    setup_envs(&mut cmd, env)
89        .args(args)
90        .stdout(Stdio::piped())
91        .stderr(Stdio::piped());
92    let child = cmd.spawn().expect("failed to execute child");
93    let output: Output = child.wait_with_output().expect("failed to wait on child");
94    //
95    OutputString {
96        status: output.status,
97        stdout: String::from(String::from_utf8_lossy(&output.stdout)),
98        stderr: String::from(String::from_utf8_lossy(&output.stderr)),
99    }
100}
101
102pub fn exec_target_with_in<I, S>(target_exe: &str, args: I, in_bytes: &[u8]) -> OutputString
103where
104    I: IntoIterator<Item = S>,
105    S: AsRef<OsStr>,
106{
107    let mut cmd: Command = Command::new(target_exe);
108    setup_envs(&mut cmd, Vec::<(&str, &str)>::new())
109        .args(args)
110        .stdin(Stdio::piped())
111        .stdout(Stdio::piped())
112        .stderr(Stdio::piped());
113    let mut child = cmd.spawn().expect("failed to execute child");
114    {
115        let stdin = child.stdin.as_mut().expect("failed to get stdin");
116        stdin.write_all(in_bytes).expect("failed to write to stdin");
117    }
118    let output: Output = child.wait_with_output().expect("failed to wait on child");
119    //
120    OutputString {
121        status: output.status,
122        stdout: String::from(String::from_utf8_lossy(&output.stdout)),
123        stderr: String::from(String::from_utf8_lossy(&output.stderr)),
124    }
125}
126
127///
128/// This invokes external a command and manipulates standard in out.
129/// You can use `std::process::Command` more easily.
130///
131/// # Example
132///
133/// ```
134/// use exec_target::exec_target_with_env_in;
135///
136/// let command = "target/debug/exe-stab-grep";
137/// let args = &["--color=always", "-e", "c"];
138/// let envs = vec![("GREP_COLORS", "ms=01;32")];
139/// let inp = b"abcdefg\n" as &[u8];
140///
141/// let oup = exec_target_with_env_in(command, args, envs, inp);
142///
143/// assert_eq!(oup.stderr, "");
144/// assert_eq!(oup.stdout, "ab\u{1b}[01;32m\u{1b}[Kc\u{1b}[m\u{1b}[Kdefg\n");
145/// assert_eq!(oup.status.success(), true);
146/// ```
147///
148pub fn exec_target_with_env_in<I, S, IKV, K, V>(
149    target_exe: &str,
150    args: I,
151    env: IKV,
152    in_bytes: &[u8],
153) -> OutputString
154where
155    I: IntoIterator<Item = S>,
156    S: AsRef<OsStr>,
157    IKV: IntoIterator<Item = (K, V)>,
158    K: AsRef<OsStr>,
159    V: AsRef<OsStr>,
160{
161    let mut cmd: Command = Command::new(target_exe);
162    setup_envs(&mut cmd, env)
163        .args(args)
164        .stdin(Stdio::piped())
165        .stdout(Stdio::piped())
166        .stderr(Stdio::piped());
167    let mut child = cmd.spawn().expect("failed to execute child");
168    {
169        let stdin = child.stdin.as_mut().expect("failed to get stdin");
170        stdin.write_all(in_bytes).expect("failed to write to stdin");
171    }
172    let output: Output = child.wait_with_output().expect("failed to wait on child");
173    //
174    OutputString {
175        status: output.status,
176        stdout: String::from(String::from_utf8_lossy(&output.stdout)),
177        stderr: String::from(String::from_utf8_lossy(&output.stderr)),
178    }
179}
180
181///
182/// parse a command line strings
183///
184/// This separates the string with blanks.
185/// This considers special characters.
186///
187/// the special characters:
188/// - "" : double quote
189/// - '' : single quote
190/// - \\ : back_slash
191///
192pub fn args_from(s: &str) -> Vec<String> {
193    let mut v: Vec<String> = Vec::new();
194    let mut ss = String::new();
195    let mut enter_q: bool = false;
196    let mut enter_qq: bool = false;
197    let mut back_slash: bool = false;
198    //
199    for c in s.chars() {
200        if back_slash {
201            ss.push(c);
202            back_slash = false;
203            continue;
204        }
205        if c == '\\' {
206            back_slash = true;
207            continue;
208        }
209        if enter_q {
210            if c == '\'' {
211                v.push(ss.clone());
212                ss.clear();
213                enter_q = false;
214            } else {
215                ss.push(c);
216            }
217            continue;
218        }
219        if enter_qq {
220            if c == '\"' {
221                v.push(ss.clone());
222                ss.clear();
223                enter_qq = false;
224            } else {
225                ss.push(c);
226            }
227            continue;
228        }
229        match c {
230            '\'' => {
231                enter_q = true;
232                continue;
233            }
234            '\"' => {
235                enter_qq = true;
236                continue;
237            }
238            ' ' => {
239                if !ss.is_empty() {
240                    v.push(ss.clone());
241                    ss.clear();
242                }
243            }
244            _ => {
245                ss.push(c);
246            }
247        }
248    }
249    if !ss.is_empty() {
250        v.push(ss.clone());
251        ss.clear();
252    }
253    //
254    v
255}