1
use std::ffi::OsStr;
2

            
3
use async_process::Command;
4

            
5
pub async fn exec<I, S>(name: &'static str, args: I) -> Result<String, String>
6
where
7
    I: IntoIterator<Item = S>,
8
    S: AsRef<OsStr>,
9
{
10
    let mut command = Command::new(name);
11

            
12
    command.args(args);
13

            
14
    trace!("Running command... {:?}", command);
15

            
16
    match command.output().await {
17
        Ok(output) => {
18
            if output.status.success() {
19
                Ok(String::from_utf8(output.stdout).unwrap())
20
            } else {
21
                Err(String::from_utf8(output.stderr).unwrap())
22
            }
23
        }
24
        Err(_) => Err(format!("Failed to call {name}")),
25
    }
26
}
27

            
28
18
pub fn join(command_args: Vec<&str>) -> Result<String, String> {
29
18
    if command_args.len() == 1 {
30
6
        return Ok(command_args[0].to_string());
31
12
    }
32

            
33
12
    let Ok(command) = shlex::try_join(command_args) else {
34
3
        return Err("Failed to parse command!".to_string());
35
    };
36

            
37
9
    Ok(command)
38
18
}
39

            
40
18
pub fn split(command: &str) -> Result<Vec<String>, String> {
41
18
    let Some(command_args) = shlex::split(&command) else {
42
3
        return Err("Failed to parse command!".to_string());
43
    };
44

            
45
15
    Ok(command_args)
46
18
}
47

            
48
#[cfg(test)]
49
mod tests {
50
    use crate::utils::command::{join, split};
51

            
52
6
    fn as_strings(values: Vec<&str>) -> Vec<String> {
53
21
        values.iter().map(|v| v.to_string()).collect()
54
6
    }
55

            
56
    #[test]
57
3
    fn join_tests() {
58
3
        assert_eq!(join(vec!["echo 'hello'"]), Ok("echo 'hello'".to_string()));
59
3
        assert_eq!(
60
3
            join(vec!["echo", "\"this\"", "is", "a", "test"]),
61
3
            Ok("echo '\"this\"' is a test".to_string())
62
        );
63
3
        assert_eq!(
64
3
            join(vec!["\0", "\0"]),
65
3
            Err("Failed to parse command!".to_string())
66
        );
67
3
    }
68

            
69
    #[test]
70
3
    fn split_tests() {
71
3
        assert_eq!(split("echo 'hello'"), Ok(as_strings(vec!["echo", "hello"])));
72
3
        assert_eq!(
73
3
            split("echo '\"this\"' is a test"),
74
3
            Ok(as_strings(vec!["echo", "\"this\"", "is", "a", "test"]))
75
        );
76
3
        assert_eq!(
77
3
            split("echo 'hello"),
78
3
            Err("Failed to parse command!".to_string())
79
        );
80
3
    }
81
}