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