docker_wrapper/command/
rm.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::{Error, Result};
7use async_trait::async_trait;
8
9#[derive(Debug, Clone)]
11pub struct RmCommand {
12 containers: Vec<String>,
14 force: bool,
16 volumes: bool,
18 link: bool,
20 pub executor: CommandExecutor,
22}
23
24impl RmCommand {
25 #[must_use]
27 pub fn new(container: impl Into<String>) -> Self {
28 Self {
29 containers: vec![container.into()],
30 force: false,
31 volumes: false,
32 link: false,
33 executor: CommandExecutor::new(),
34 }
35 }
36
37 #[must_use]
39 pub fn new_multiple(containers: Vec<impl Into<String>>) -> Self {
40 Self {
41 containers: containers.into_iter().map(Into::into).collect(),
42 force: false,
43 volumes: false,
44 link: false,
45 executor: CommandExecutor::new(),
46 }
47 }
48
49 #[must_use]
51 pub fn container(mut self, container: impl Into<String>) -> Self {
52 self.containers.push(container.into());
53 self
54 }
55
56 #[must_use]
58 pub fn force(mut self) -> Self {
59 self.force = true;
60 self
61 }
62
63 #[must_use]
65 pub fn volumes(mut self) -> Self {
66 self.volumes = true;
67 self
68 }
69
70 #[must_use]
72 pub fn link(mut self) -> Self {
73 self.link = true;
74 self
75 }
76
77 pub async fn run(&self) -> Result<RmResult> {
86 let output = self.execute().await?;
87
88 let removed: Vec<String> = output
90 .stdout
91 .lines()
92 .filter(|line| !line.is_empty())
93 .map(String::from)
94 .collect();
95
96 Ok(RmResult { removed, output })
97 }
98}
99
100#[async_trait]
101impl DockerCommand for RmCommand {
102 type Output = CommandOutput;
103
104 fn get_executor(&self) -> &CommandExecutor {
105 &self.executor
106 }
107
108 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
109 &mut self.executor
110 }
111
112 fn build_command_args(&self) -> Vec<String> {
113 let mut args = vec!["rm".to_string()];
114
115 if self.force {
116 args.push("--force".to_string());
117 }
118
119 if self.volumes {
120 args.push("--volumes".to_string());
121 }
122
123 if self.link {
124 args.push("--link".to_string());
125 }
126
127 args.extend(self.containers.clone());
129 args.extend(self.executor.raw_args.clone());
130
131 args
132 }
133
134 async fn execute(&self) -> Result<Self::Output> {
135 if self.containers.is_empty() {
136 return Err(Error::invalid_config("No containers specified for removal"));
137 }
138
139 let args = self.build_command_args();
140 let command_name = args[0].clone();
141 let command_args = args[1..].to_vec();
142 self.executor
143 .execute_command(&command_name, command_args)
144 .await
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct RmResult {
151 pub removed: Vec<String>,
153 pub output: CommandOutput,
155}
156
157impl RmResult {
158 #[must_use]
160 pub fn all_removed(&self) -> bool {
161 self.output.success
162 }
163
164 #[must_use]
166 pub fn count(&self) -> usize {
167 self.removed.len()
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_rm_single_container() {
177 let cmd = RmCommand::new("test-container");
178 let args = cmd.build_command_args();
179 assert_eq!(args, vec!["rm", "test-container"]);
180 }
181
182 #[test]
183 fn test_rm_multiple_containers() {
184 let cmd = RmCommand::new_multiple(vec!["container1", "container2", "container3"]);
185 let args = cmd.build_command_args();
186 assert_eq!(args, vec!["rm", "container1", "container2", "container3"]);
187 }
188
189 #[test]
190 fn test_rm_with_force() {
191 let cmd = RmCommand::new("test-container").force();
192 let args = cmd.build_command_args();
193 assert_eq!(args, vec!["rm", "--force", "test-container"]);
194 }
195
196 #[test]
197 fn test_rm_with_volumes() {
198 let cmd = RmCommand::new("test-container").volumes();
199 let args = cmd.build_command_args();
200 assert_eq!(args, vec!["rm", "--volumes", "test-container"]);
201 }
202
203 #[test]
204 fn test_rm_with_all_options() {
205 let cmd = RmCommand::new("test-container").force().volumes().link();
206 let args = cmd.build_command_args();
207 assert_eq!(
208 args,
209 vec!["rm", "--force", "--volumes", "--link", "test-container"]
210 );
211 }
212
213 #[test]
214 fn test_rm_builder_chain() {
215 let cmd = RmCommand::new("container1")
216 .container("container2")
217 .container("container3")
218 .force()
219 .volumes();
220 let args = cmd.build_command_args();
221 assert_eq!(
222 args,
223 vec![
224 "rm",
225 "--force",
226 "--volumes",
227 "container1",
228 "container2",
229 "container3"
230 ]
231 );
232 }
233}