1use std::env;
2use std::process::Command;
3
4#[derive(thiserror::Error, Debug)]
5pub enum Error {
6 #[error("io error")]
7 Io(#[from] std::io::Error),
8 #[error("key is empty")]
9 EmptyKey,
10 #[error("command error")]
11 CommandError,
12}
13
14pub fn parse_input<T: AsRef<str>>(args: &[T]) -> (Vec<(&str, &str)>, usize) {
15 let mut idx = 0;
16 let mut v = vec![];
17 for i in args {
18 if let Some(pair) = i.as_ref().split_once('=') {
19 v.push(pair);
20 idx += 1;
21 } else {
22 break;
23 }
24 }
25 (v, idx)
26}
27
28pub fn run(cmd: &str, cmd_args: &[String], envs: &Vec<(&str, &str)>) -> Result<(), Error> {
29 let mut cmd = Command::new(cmd);
30
31 for (key, value) in envs {
32 if key.is_empty() {
33 return Err(Error::EmptyKey);
34 }
35 cmd.env(key, value);
36 }
37
38 cmd.stdin(std::process::Stdio::inherit())
39 .stdout(std::process::Stdio::inherit())
40 .stderr(std::process::Stdio::inherit());
41
42 cmd.args(cmd_args);
43
44 cmd.output()?;
45 Ok(())
46}
47
48pub fn cross_env() -> Result<(), Error> {
49 let args: Vec<String> = env::args().skip(1).collect();
50 let (envs, idx) = parse_input(&args);
51
52 let cmd = &args[idx];
53 let cmd_args = &args[idx + 1..];
54 run(cmd, cmd_args, &envs)
55}
56
57#[cfg(test)]
58mod test {
59 use crate::parse_input;
60
61 #[test]
62 fn test_parse_input() {
63 for (args, expect_envs, expect_idx) in [
64 (vec!["a=1"], vec![("a", "1")], 1),
65 (vec!["a=1", "b=2"], vec![("a", "1"), ("b", "2")], 2),
66 ] {
67 let (envs, idx) = parse_input(&args);
68 assert_eq!(envs, expect_envs);
69 assert_eq!(idx, expect_idx);
70 }
71 }
72}