mk_lib/schema/
shell.rs

1use serde::Deserialize;
2use std::process::Command as ProcessCommand;
3
4#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
5pub struct ShellArgs {
6  /// The shell command to run
7  pub command: String,
8
9  /// The flags to pass to the shell command
10  pub args: Option<Vec<String>>,
11}
12
13#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
14#[serde(untagged)]
15pub enum Shell {
16  String(String),
17  Shell(Box<ShellArgs>),
18}
19
20impl Default for Shell {
21  fn default() -> Self {
22    Shell::String("sh".to_string())
23  }
24}
25
26impl Shell {
27  pub fn new() -> anyhow::Result<Self> {
28    Ok(Shell::default())
29  }
30
31  pub fn new_with_flags(command: &str, args: Vec<String>) -> anyhow::Result<Self> {
32    let shell_def = ShellArgs {
33      command: command.to_string(),
34      args: Some(args),
35    };
36    Ok(Shell::Shell(Box::new(shell_def)))
37  }
38
39  pub fn from_shell(shell: &Shell) -> Self {
40    match shell {
41      Shell::String(command) => Shell::String(command.to_string()),
42      Shell::Shell(args) => Shell::Shell(args.clone()),
43    }
44  }
45
46  pub fn cmd(&self) -> String {
47    match self {
48      Shell::String(command) => ShellArgs {
49        command: command.to_string(),
50        args: None,
51      }
52      .cmd(),
53      Shell::Shell(args) => args.cmd(),
54    }
55  }
56
57  pub fn args(&self) -> Vec<String> {
58    match self {
59      Shell::String(command) => ShellArgs {
60        command: command.to_string(),
61        args: None,
62      }
63      .shell_args(),
64      Shell::Shell(args) => args.shell_args(),
65    }
66  }
67
68  pub fn proc(&self) -> ProcessCommand {
69    let shell = self.cmd();
70    let args = self.args();
71
72    let mut cmd = ProcessCommand::new(&shell);
73    for arg in args {
74      cmd.arg(arg);
75    }
76
77    cmd
78  }
79}
80
81impl From<Shell> for ProcessCommand {
82  fn from(shell: Shell) -> Self {
83    shell.proc()
84  }
85}
86
87impl ShellArgs {
88  pub fn cmd(&self) -> String {
89    self.command.clone()
90  }
91
92  pub fn shell_args(&self) -> Vec<String> {
93    let command = self.command.clone();
94    let args = self.args.clone().unwrap_or_default();
95    let posix_shell = ["sh", "bash", "zsh", "fish"];
96
97    // If the shell is not a POSIX shell, we don't need to add the `-c` flag
98    // to the command. We can just return the arguments as is.
99    if !posix_shell.contains(&command.as_str()) {
100      return args;
101    }
102
103    // If the shell is a POSIX shell, we need to add the `-c` flag
104    // to the command. If it's already present, we don't need to add it.
105    if args.iter().any(|arg| arg == "-c") {
106      return args;
107    }
108
109    // If the `-c` flag is not present, we need to add it
110    let mut args = args;
111    args.push("-c".to_string());
112    args
113  }
114}