docker_wrapper/command/
compose_start.rs1use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone)]
9pub struct ComposeStartCommand {
10 pub executor: CommandExecutor,
12 pub config: ComposeConfig,
14 pub services: Vec<String>,
16}
17
18#[derive(Debug, Clone)]
20pub struct ComposeStartResult {
21 pub stdout: String,
23 pub stderr: String,
25 pub success: bool,
27 pub services: Vec<String>,
29}
30
31impl ComposeStartCommand {
32 #[must_use]
34 pub fn new() -> Self {
35 Self {
36 executor: CommandExecutor::new(),
37 config: ComposeConfig::new(),
38 services: Vec::new(),
39 }
40 }
41
42 #[must_use]
44 pub fn service(mut self, service: impl Into<String>) -> Self {
45 self.services.push(service.into());
46 self
47 }
48
49 #[must_use]
51 pub fn services<I, S>(mut self, services: I) -> Self
52 where
53 I: IntoIterator<Item = S>,
54 S: Into<String>,
55 {
56 self.services.extend(services.into_iter().map(Into::into));
57 self
58 }
59}
60
61impl Default for ComposeStartCommand {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67#[async_trait]
68impl DockerCommand for ComposeStartCommand {
69 type Output = ComposeStartResult;
70
71 fn get_executor(&self) -> &CommandExecutor {
72 &self.executor
73 }
74
75 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
76 &mut self.executor
77 }
78
79 fn build_command_args(&self) -> Vec<String> {
80 <Self as ComposeCommand>::build_command_args(self)
82 }
83
84 async fn execute(&self) -> Result<Self::Output> {
85 let args = <Self as ComposeCommand>::build_command_args(self);
86 let output = self.execute_command(args).await?;
87
88 Ok(ComposeStartResult {
89 stdout: output.stdout,
90 stderr: output.stderr,
91 success: output.success,
92 services: self.services.clone(),
93 })
94 }
95}
96
97impl ComposeCommand for ComposeStartCommand {
98 fn get_config(&self) -> &ComposeConfig {
99 &self.config
100 }
101
102 fn get_config_mut(&mut self) -> &mut ComposeConfig {
103 &mut self.config
104 }
105
106 fn subcommand(&self) -> &'static str {
107 "start"
108 }
109
110 fn build_subcommand_args(&self) -> Vec<String> {
111 self.services.clone()
113 }
114}
115
116impl ComposeStartResult {
117 #[must_use]
119 pub fn success(&self) -> bool {
120 self.success
121 }
122
123 #[must_use]
125 pub fn services(&self) -> &[String] {
126 &self.services
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_compose_start_basic() {
136 let cmd = ComposeStartCommand::new();
137 let args = cmd.build_subcommand_args();
138 assert!(args.is_empty());
139
140 let full_args = ComposeCommand::build_command_args(&cmd);
141 assert_eq!(full_args[0], "compose");
142 assert!(full_args.contains(&"start".to_string()));
143 }
144
145 #[test]
146 fn test_compose_start_with_services() {
147 let cmd = ComposeStartCommand::new()
148 .service("web")
149 .service("db")
150 .service("cache");
151
152 let args = cmd.build_subcommand_args();
153 assert_eq!(args, vec!["web", "db", "cache"]);
154 }
155
156 #[test]
157 fn test_compose_start_single_service() {
158 let cmd = ComposeStartCommand::new().service("postgres");
159 let args = cmd.build_subcommand_args();
160 assert_eq!(args, vec!["postgres"]);
161 }
162
163 #[test]
164 fn test_compose_start_with_services_method() {
165 let cmd = ComposeStartCommand::new().services(vec!["frontend", "backend"]);
166 let args = cmd.build_subcommand_args();
167 assert_eq!(args, vec!["frontend", "backend"]);
168 }
169
170 #[test]
171 fn test_compose_config_integration() {
172 let cmd = ComposeStartCommand::new()
173 .file("docker-compose.yml")
174 .project_name("myapp")
175 .service("api");
176
177 let args = ComposeCommand::build_command_args(&cmd);
178 assert!(args.contains(&"--file".to_string()));
179 assert!(args.contains(&"docker-compose.yml".to_string()));
180 assert!(args.contains(&"--project-name".to_string()));
181 assert!(args.contains(&"myapp".to_string()));
182 assert!(args.contains(&"api".to_string()));
183 }
184}