cross_env/
lib.rs

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}