docker_wrapper/command/
compose_stop.rs1use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6use std::time::Duration;
7
8#[derive(Debug, Clone)]
10pub struct ComposeStopCommand {
11 pub executor: CommandExecutor,
13 pub config: ComposeConfig,
15 pub services: Vec<String>,
17 pub timeout: Option<Duration>,
19}
20
21#[derive(Debug, Clone)]
23pub struct ComposeStopResult {
24 pub stdout: String,
26 pub stderr: String,
28 pub success: bool,
30 pub services: Vec<String>,
32}
33
34impl ComposeStopCommand {
35 #[must_use]
37 pub fn new() -> Self {
38 Self {
39 executor: CommandExecutor::new(),
40 config: ComposeConfig::new(),
41 services: Vec::new(),
42 timeout: None,
43 }
44 }
45
46 #[must_use]
48 pub fn service(mut self, service: impl Into<String>) -> Self {
49 self.services.push(service.into());
50 self
51 }
52
53 #[must_use]
55 pub fn services<I, S>(mut self, services: I) -> Self
56 where
57 I: IntoIterator<Item = S>,
58 S: Into<String>,
59 {
60 self.services.extend(services.into_iter().map(Into::into));
61 self
62 }
63
64 #[must_use]
66 pub fn timeout(mut self, timeout: Duration) -> Self {
67 self.timeout = Some(timeout);
68 self
69 }
70}
71
72impl Default for ComposeStopCommand {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78#[async_trait]
79impl DockerCommand for ComposeStopCommand {
80 type Output = ComposeStopResult;
81
82 fn get_executor(&self) -> &CommandExecutor {
83 &self.executor
84 }
85
86 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
87 &mut self.executor
88 }
89
90 fn build_command_args(&self) -> Vec<String> {
91 <Self as ComposeCommand>::build_command_args(self)
93 }
94
95 async fn execute(&self) -> Result<Self::Output> {
96 let args = <Self as ComposeCommand>::build_command_args(self);
97 let output = self.execute_command(args).await?;
98
99 Ok(ComposeStopResult {
100 stdout: output.stdout,
101 stderr: output.stderr,
102 success: output.success,
103 services: self.services.clone(),
104 })
105 }
106}
107
108impl ComposeCommand for ComposeStopCommand {
109 fn get_config(&self) -> &ComposeConfig {
110 &self.config
111 }
112
113 fn get_config_mut(&mut self) -> &mut ComposeConfig {
114 &mut self.config
115 }
116
117 fn subcommand(&self) -> &'static str {
118 "stop"
119 }
120
121 fn build_subcommand_args(&self) -> Vec<String> {
122 let mut args = Vec::new();
123
124 if let Some(timeout) = self.timeout {
125 args.push("--timeout".to_string());
126 args.push(timeout.as_secs().to_string());
127 }
128
129 args.extend(self.services.clone());
131
132 args
133 }
134}
135
136impl ComposeStopResult {
137 #[must_use]
139 pub fn success(&self) -> bool {
140 self.success
141 }
142
143 #[must_use]
145 pub fn services(&self) -> &[String] {
146 &self.services
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_compose_stop_basic() {
156 let cmd = ComposeStopCommand::new();
157 let args = cmd.build_subcommand_args();
158 assert!(args.is_empty());
159
160 let full_args = ComposeCommand::build_command_args(&cmd);
161 assert_eq!(full_args[0], "compose");
162 assert!(full_args.contains(&"stop".to_string()));
163 }
164
165 #[test]
166 fn test_compose_stop_with_services() {
167 let cmd = ComposeStopCommand::new().service("web").service("worker");
168 let args = cmd.build_subcommand_args();
169 assert_eq!(args, vec!["web", "worker"]);
170 }
171
172 #[test]
173 fn test_compose_stop_with_timeout() {
174 let cmd = ComposeStopCommand::new()
175 .timeout(Duration::from_secs(10))
176 .service("db");
177
178 let args = cmd.build_subcommand_args();
179 assert_eq!(args, vec!["--timeout", "10", "db"]);
180 }
181
182 #[test]
183 fn test_compose_stop_with_services_method() {
184 let cmd = ComposeStopCommand::new().services(vec!["redis", "postgres"]);
185 let args = cmd.build_subcommand_args();
186 assert_eq!(args, vec!["redis", "postgres"]);
187 }
188
189 #[test]
190 fn test_compose_stop_zero_timeout() {
191 let cmd = ComposeStopCommand::new()
192 .timeout(Duration::from_secs(0))
193 .service("app");
194
195 let args = cmd.build_subcommand_args();
196 assert_eq!(args, vec!["--timeout", "0", "app"]);
197 }
198
199 #[test]
200 fn test_compose_stop_all_services_with_timeout() {
201 let cmd = ComposeStopCommand::new().timeout(Duration::from_secs(30));
202 let args = cmd.build_subcommand_args();
203 assert_eq!(args, vec!["--timeout", "30"]);
204 }
205
206 #[test]
207 fn test_compose_config_integration() {
208 let cmd = ComposeStopCommand::new()
209 .file("compose.yml")
210 .project_name("webapp")
211 .timeout(Duration::from_secs(5))
212 .service("redis");
213
214 let args = ComposeCommand::build_command_args(&cmd);
215 assert!(args.contains(&"--file".to_string()));
216 assert!(args.contains(&"compose.yml".to_string()));
217 assert!(args.contains(&"--project-name".to_string()));
218 assert!(args.contains(&"webapp".to_string()));
219 assert!(args.contains(&"--timeout".to_string()));
220 assert!(args.contains(&"5".to_string()));
221 assert!(args.contains(&"redis".to_string()));
222 }
223}