docker_wrapper/command/
rm.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
53use crate::error::{Error, Result};
54use async_trait::async_trait;
55
56#[derive(Debug, Clone)]
58pub struct RmCommand {
59 containers: Vec<String>,
61 force: bool,
63 volumes: bool,
65 link: bool,
67 pub executor: CommandExecutor,
69}
70
71impl RmCommand {
72 #[must_use]
74 pub fn new(container: impl Into<String>) -> Self {
75 Self {
76 containers: vec![container.into()],
77 force: false,
78 volumes: false,
79 link: false,
80 executor: CommandExecutor::new(),
81 }
82 }
83
84 #[must_use]
86 pub fn new_multiple(containers: Vec<impl Into<String>>) -> Self {
87 Self {
88 containers: containers.into_iter().map(Into::into).collect(),
89 force: false,
90 volumes: false,
91 link: false,
92 executor: CommandExecutor::new(),
93 }
94 }
95
96 #[must_use]
98 pub fn container(mut self, container: impl Into<String>) -> Self {
99 self.containers.push(container.into());
100 self
101 }
102
103 #[must_use]
105 pub fn force(mut self) -> Self {
106 self.force = true;
107 self
108 }
109
110 #[must_use]
112 pub fn volumes(mut self) -> Self {
113 self.volumes = true;
114 self
115 }
116
117 #[must_use]
119 pub fn link(mut self) -> Self {
120 self.link = true;
121 self
122 }
123
124 pub async fn run(&self) -> Result<RmResult> {
133 let output = self.execute().await?;
134
135 let removed: Vec<String> = output
137 .stdout
138 .lines()
139 .filter(|line| !line.is_empty())
140 .map(String::from)
141 .collect();
142
143 Ok(RmResult { removed, output })
144 }
145}
146
147#[async_trait]
148impl DockerCommand for RmCommand {
149 type Output = CommandOutput;
150
151 fn get_executor(&self) -> &CommandExecutor {
152 &self.executor
153 }
154
155 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
156 &mut self.executor
157 }
158
159 fn build_command_args(&self) -> Vec<String> {
160 let mut args = vec!["rm".to_string()];
161
162 if self.force {
163 args.push("--force".to_string());
164 }
165
166 if self.volumes {
167 args.push("--volumes".to_string());
168 }
169
170 if self.link {
171 args.push("--link".to_string());
172 }
173
174 args.extend(self.containers.clone());
176 args.extend(self.executor.raw_args.clone());
177
178 args
179 }
180
181 async fn execute(&self) -> Result<Self::Output> {
182 if self.containers.is_empty() {
183 return Err(Error::invalid_config("No containers specified for removal"));
184 }
185
186 let args = self.build_command_args();
187 let command_name = args[0].clone();
188 let command_args = args[1..].to_vec();
189 self.executor
190 .execute_command(&command_name, command_args)
191 .await
192 }
193}
194
195#[derive(Debug, Clone)]
197pub struct RmResult {
198 pub removed: Vec<String>,
200 pub output: CommandOutput,
202}
203
204impl RmResult {
205 #[must_use]
207 pub fn all_removed(&self) -> bool {
208 self.output.success
209 }
210
211 #[must_use]
213 pub fn count(&self) -> usize {
214 self.removed.len()
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_rm_single_container() {
224 let cmd = RmCommand::new("test-container");
225 let args = cmd.build_command_args();
226 assert_eq!(args, vec!["rm", "test-container"]);
227 }
228
229 #[test]
230 fn test_rm_multiple_containers() {
231 let cmd = RmCommand::new_multiple(vec!["container1", "container2", "container3"]);
232 let args = cmd.build_command_args();
233 assert_eq!(args, vec!["rm", "container1", "container2", "container3"]);
234 }
235
236 #[test]
237 fn test_rm_with_force() {
238 let cmd = RmCommand::new("test-container").force();
239 let args = cmd.build_command_args();
240 assert_eq!(args, vec!["rm", "--force", "test-container"]);
241 }
242
243 #[test]
244 fn test_rm_with_volumes() {
245 let cmd = RmCommand::new("test-container").volumes();
246 let args = cmd.build_command_args();
247 assert_eq!(args, vec!["rm", "--volumes", "test-container"]);
248 }
249
250 #[test]
251 fn test_rm_with_all_options() {
252 let cmd = RmCommand::new("test-container").force().volumes().link();
253 let args = cmd.build_command_args();
254 assert_eq!(
255 args,
256 vec!["rm", "--force", "--volumes", "--link", "test-container"]
257 );
258 }
259
260 #[test]
261 fn test_rm_builder_chain() {
262 let cmd = RmCommand::new("container1")
263 .container("container2")
264 .container("container3")
265 .force()
266 .volumes();
267 let args = cmd.build_command_args();
268 assert_eq!(
269 args,
270 vec![
271 "rm",
272 "--force",
273 "--volumes",
274 "container1",
275 "container2",
276 "container3"
277 ]
278 );
279 }
280}