docker_wrapper/command/
attach.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::Result;
7use async_trait::async_trait;
8
9#[derive(Debug, Clone)]
33pub struct AttachCommand {
34 container: String,
36 detach_keys: Option<String>,
38 no_stdin: bool,
40 sig_proxy: bool,
42 pub executor: CommandExecutor,
44}
45
46impl AttachCommand {
47 #[must_use]
57 pub fn new(container: impl Into<String>) -> Self {
58 Self {
59 container: container.into(),
60 detach_keys: None,
61 no_stdin: false,
62 sig_proxy: true, executor: CommandExecutor::new(),
64 }
65 }
66
67 #[must_use]
78 pub fn detach_keys(mut self, keys: impl Into<String>) -> Self {
79 self.detach_keys = Some(keys.into());
80 self
81 }
82
83 #[must_use]
94 pub fn no_stdin(mut self) -> Self {
95 self.no_stdin = true;
96 self
97 }
98
99 #[must_use]
110 pub fn no_sig_proxy(mut self) -> Self {
111 self.sig_proxy = false;
112 self
113 }
114
115 pub async fn run(&self) -> Result<AttachResult> {
140 let output = self.execute().await?;
141 Ok(AttachResult {
142 output,
143 container: self.container.clone(),
144 })
145 }
146}
147
148#[async_trait]
149impl DockerCommand for AttachCommand {
150 type Output = CommandOutput;
151
152 fn get_executor(&self) -> &CommandExecutor {
153 &self.executor
154 }
155
156 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
157 &mut self.executor
158 }
159
160 fn build_command_args(&self) -> Vec<String> {
161 let mut args = vec!["attach".to_string()];
162
163 if let Some(ref keys) = self.detach_keys {
164 args.push("--detach-keys".to_string());
165 args.push(keys.clone());
166 }
167
168 if self.no_stdin {
169 args.push("--no-stdin".to_string());
170 }
171
172 if !self.sig_proxy {
173 args.push("--sig-proxy=false".to_string());
174 }
175
176 args.push(self.container.clone());
178
179 args.extend(self.executor.raw_args.clone());
181
182 args
183 }
184
185 async fn execute(&self) -> Result<Self::Output> {
186 let args = self.build_command_args();
187 self.execute_command(args).await
188 }
189}
190
191#[derive(Debug, Clone)]
193pub struct AttachResult {
194 pub output: CommandOutput,
196 pub container: String,
198}
199
200impl AttachResult {
201 #[must_use]
203 pub fn success(&self) -> bool {
204 self.output.success
205 }
206
207 #[must_use]
209 pub fn container(&self) -> &str {
210 &self.container
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_attach_basic() {
220 let cmd = AttachCommand::new("test-container");
221 let args = cmd.build_command_args();
222 assert_eq!(args, vec!["attach", "test-container"]);
223 }
224
225 #[test]
226 fn test_attach_with_detach_keys() {
227 let cmd = AttachCommand::new("test-container").detach_keys("ctrl-a,ctrl-d");
228 let args = cmd.build_command_args();
229 assert_eq!(
230 args,
231 vec!["attach", "--detach-keys", "ctrl-a,ctrl-d", "test-container"]
232 );
233 }
234
235 #[test]
236 fn test_attach_no_stdin() {
237 let cmd = AttachCommand::new("test-container").no_stdin();
238 let args = cmd.build_command_args();
239 assert_eq!(args, vec!["attach", "--no-stdin", "test-container"]);
240 }
241
242 #[test]
243 fn test_attach_no_sig_proxy() {
244 let cmd = AttachCommand::new("test-container").no_sig_proxy();
245 let args = cmd.build_command_args();
246 assert_eq!(args, vec!["attach", "--sig-proxy=false", "test-container"]);
247 }
248
249 #[test]
250 fn test_attach_all_options() {
251 let cmd = AttachCommand::new("test-container")
252 .detach_keys("ctrl-x,ctrl-y")
253 .no_stdin()
254 .no_sig_proxy();
255 let args = cmd.build_command_args();
256 assert_eq!(
257 args,
258 vec![
259 "attach",
260 "--detach-keys",
261 "ctrl-x,ctrl-y",
262 "--no-stdin",
263 "--sig-proxy=false",
264 "test-container"
265 ]
266 );
267 }
268}