shell_tunnel/execution/
command.rs1use std::collections::HashMap;
4use std::path::PathBuf;
5use std::time::Duration;
6
7#[derive(Debug, Clone)]
9pub struct Command {
10 pub command_line: String,
12 pub working_dir: Option<PathBuf>,
14 pub env: HashMap<String, String>,
16 pub timeout: Option<Duration>,
18 pub capture_output: bool,
20}
21
22impl Command {
23 pub fn new(command_line: impl Into<String>) -> Self {
25 Self {
26 command_line: command_line.into(),
27 working_dir: None,
28 env: HashMap::new(),
29 timeout: None,
30 capture_output: true,
31 }
32 }
33
34 pub fn working_dir(mut self, dir: impl Into<PathBuf>) -> Self {
36 self.working_dir = Some(dir.into());
37 self
38 }
39
40 pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
42 self.env.insert(key.into(), value.into());
43 self
44 }
45
46 pub fn envs<I, K, V>(mut self, vars: I) -> Self
48 where
49 I: IntoIterator<Item = (K, V)>,
50 K: Into<String>,
51 V: Into<String>,
52 {
53 for (k, v) in vars {
54 self.env.insert(k.into(), v.into());
55 }
56 self
57 }
58
59 pub fn timeout(mut self, duration: Duration) -> Self {
61 self.timeout = Some(duration);
62 self
63 }
64
65 pub fn capture_output(mut self, capture: bool) -> Self {
67 self.capture_output = capture;
68 self
69 }
70}
71
72impl Default for Command {
73 fn default() -> Self {
74 Self::new("")
75 }
76}
77
78#[derive(Debug, Default)]
80pub struct CommandBuilder {
81 command_line: Option<String>,
82 working_dir: Option<PathBuf>,
83 env: HashMap<String, String>,
84 timeout: Option<Duration>,
85 capture_output: bool,
86}
87
88impl CommandBuilder {
89 pub fn new() -> Self {
91 Self {
92 capture_output: true,
93 ..Default::default()
94 }
95 }
96
97 pub fn command_line(mut self, cmd: impl Into<String>) -> Self {
99 self.command_line = Some(cmd.into());
100 self
101 }
102
103 pub fn working_dir(mut self, dir: impl Into<PathBuf>) -> Self {
105 self.working_dir = Some(dir.into());
106 self
107 }
108
109 pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
111 self.env.insert(key.into(), value.into());
112 self
113 }
114
115 pub fn timeout(mut self, duration: Duration) -> Self {
117 self.timeout = Some(duration);
118 self
119 }
120
121 pub fn capture_output(mut self, capture: bool) -> Self {
123 self.capture_output = capture;
124 self
125 }
126
127 pub fn build(self) -> Option<Command> {
131 self.command_line.map(|cmd| Command {
132 command_line: cmd,
133 working_dir: self.working_dir,
134 env: self.env,
135 timeout: self.timeout,
136 capture_output: self.capture_output,
137 })
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_command_new() {
147 let cmd = Command::new("ls -la");
148 assert_eq!(cmd.command_line, "ls -la");
149 assert!(cmd.working_dir.is_none());
150 assert!(cmd.env.is_empty());
151 assert!(cmd.timeout.is_none());
152 assert!(cmd.capture_output);
153 }
154
155 #[test]
156 fn test_command_builder_chain() {
157 let cmd = Command::new("cargo build")
158 .working_dir("/project")
159 .env("RUST_LOG", "debug")
160 .timeout(Duration::from_secs(60))
161 .capture_output(true);
162
163 assert_eq!(cmd.command_line, "cargo build");
164 assert_eq!(cmd.working_dir, Some(PathBuf::from("/project")));
165 assert_eq!(cmd.env.get("RUST_LOG"), Some(&"debug".to_string()));
166 assert_eq!(cmd.timeout, Some(Duration::from_secs(60)));
167 }
168
169 #[test]
170 fn test_command_envs() {
171 let vars = [("KEY1", "val1"), ("KEY2", "val2")];
172 let cmd = Command::new("echo").envs(vars);
173
174 assert_eq!(cmd.env.len(), 2);
175 assert_eq!(cmd.env.get("KEY1"), Some(&"val1".to_string()));
176 assert_eq!(cmd.env.get("KEY2"), Some(&"val2".to_string()));
177 }
178
179 #[test]
180 fn test_command_builder_build() {
181 let cmd = CommandBuilder::new()
182 .command_line("pwd")
183 .working_dir("/tmp")
184 .build();
185
186 assert!(cmd.is_some());
187 let cmd = cmd.unwrap();
188 assert_eq!(cmd.command_line, "pwd");
189 assert_eq!(cmd.working_dir, Some(PathBuf::from("/tmp")));
190 }
191
192 #[test]
193 fn test_command_builder_empty() {
194 let cmd = CommandBuilder::new().build();
195 assert!(cmd.is_none());
196 }
197}