1use serde::Deserialize;
2use std::process::Command as ProcessCommand;
3
4#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
5pub struct ShellArgs {
6 pub command: String,
8
9 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 !posix_shell.contains(&command.as_str()) {
100 return args;
101 }
102
103 if args.iter().any(|arg| arg == "-c") {
106 return args;
107 }
108
109 let mut args = args;
111 args.push("-c".to_string());
112 args
113 }
114}