docker_wrapper/compose/
create.rs1use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone, Default)]
11#[allow(clippy::struct_excessive_bools)]
12pub struct ComposeCreateCommand {
13 pub config: ComposeConfig,
15 pub build: bool,
17 pub no_build: bool,
19 pub force_recreate: bool,
21 pub no_recreate: bool,
23 pub pull: Option<PullPolicy>,
25 pub remove_orphans: bool,
27 pub services: Vec<String>,
29}
30
31#[derive(Debug, Clone, Copy)]
33pub enum PullPolicy {
34 Always,
36 Never,
38 Missing,
40 Build,
42}
43
44impl PullPolicy {
45 #[must_use]
47 pub fn as_arg(&self) -> &str {
48 match self {
49 Self::Always => "always",
50 Self::Never => "never",
51 Self::Missing => "missing",
52 Self::Build => "build",
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct CreateResult {
60 pub output: String,
62 pub success: bool,
64}
65
66impl ComposeCreateCommand {
67 #[must_use]
69 pub fn new() -> Self {
70 Self::default()
71 }
72
73 #[must_use]
75 pub fn file<P: Into<std::path::PathBuf>>(mut self, file: P) -> Self {
76 self.config.files.push(file.into());
77 self
78 }
79
80 #[must_use]
82 pub fn project_name(mut self, name: impl Into<String>) -> Self {
83 self.config.project_name = Some(name.into());
84 self
85 }
86
87 #[must_use]
89 pub fn build(mut self) -> Self {
90 self.build = true;
91 self
92 }
93
94 #[must_use]
96 pub fn no_build(mut self) -> Self {
97 self.no_build = true;
98 self
99 }
100
101 #[must_use]
103 pub fn force_recreate(mut self) -> Self {
104 self.force_recreate = true;
105 self
106 }
107
108 #[must_use]
110 pub fn no_recreate(mut self) -> Self {
111 self.no_recreate = true;
112 self
113 }
114
115 #[must_use]
117 pub fn pull(mut self, policy: PullPolicy) -> Self {
118 self.pull = Some(policy);
119 self
120 }
121
122 #[must_use]
124 pub fn remove_orphans(mut self) -> Self {
125 self.remove_orphans = true;
126 self
127 }
128
129 #[must_use]
131 pub fn service(mut self, service: impl Into<String>) -> Self {
132 self.services.push(service.into());
133 self
134 }
135
136 #[must_use]
138 pub fn services<I, S>(mut self, services: I) -> Self
139 where
140 I: IntoIterator<Item = S>,
141 S: Into<String>,
142 {
143 self.services.extend(services.into_iter().map(Into::into));
144 self
145 }
146
147 fn build_args(&self) -> Vec<String> {
148 let mut args = vec!["create".to_string()];
149
150 if self.build {
152 args.push("--build".to_string());
153 }
154 if self.no_build {
155 args.push("--no-build".to_string());
156 }
157 if self.force_recreate {
158 args.push("--force-recreate".to_string());
159 }
160 if self.no_recreate {
161 args.push("--no-recreate".to_string());
162 }
163 if self.remove_orphans {
164 args.push("--remove-orphans".to_string());
165 }
166
167 if let Some(pull) = &self.pull {
169 args.push("--pull".to_string());
170 args.push(pull.as_arg().to_string());
171 }
172
173 args.extend(self.services.clone());
175
176 args
177 }
178}
179
180#[async_trait]
181impl ComposeCommand for ComposeCreateCommand {
182 type Output = CreateResult;
183
184 fn get_config(&self) -> &ComposeConfig {
185 &self.config
186 }
187
188 fn get_config_mut(&mut self) -> &mut ComposeConfig {
189 &mut self.config
190 }
191
192 async fn execute_compose(&self, args: Vec<String>) -> Result<Self::Output> {
193 let output = self.execute_compose_command(args).await?;
194
195 Ok(CreateResult {
196 output: output.stdout,
197 success: output.success,
198 })
199 }
200
201 async fn execute(&self) -> Result<Self::Output> {
202 let args = self.build_args();
203 self.execute_compose(args).await
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_create_command_basic() {
213 let cmd = ComposeCreateCommand::new();
214 let args = cmd.build_args();
215 assert_eq!(args[0], "create");
216 }
217
218 #[test]
219 fn test_create_command_with_build() {
220 let cmd = ComposeCreateCommand::new().build().force_recreate();
221 let args = cmd.build_args();
222 assert!(args.contains(&"--build".to_string()));
223 assert!(args.contains(&"--force-recreate".to_string()));
224 }
225
226 #[test]
227 fn test_create_command_with_pull() {
228 let cmd = ComposeCreateCommand::new()
229 .pull(PullPolicy::Always)
230 .no_recreate();
231 let args = cmd.build_args();
232 assert!(args.contains(&"--pull".to_string()));
233 assert!(args.contains(&"always".to_string()));
234 assert!(args.contains(&"--no-recreate".to_string()));
235 }
236
237 #[test]
238 fn test_create_command_with_services() {
239 let cmd = ComposeCreateCommand::new()
240 .service("web")
241 .service("db")
242 .remove_orphans();
243 let args = cmd.build_args();
244 assert!(args.contains(&"web".to_string()));
245 assert!(args.contains(&"db".to_string()));
246 assert!(args.contains(&"--remove-orphans".to_string()));
247 }
248}