docker_wrapper/command/builder/
rm.rs

1//! Docker buildx rm command implementation.
2
3use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Result of buildx rm command
8#[derive(Debug, Clone)]
9pub struct BuildxRmResult {
10    /// The names of the builders that were removed
11    pub names: Vec<String>,
12    /// Raw output from the command
13    pub output: String,
14    /// Whether the command succeeded
15    pub success: bool,
16}
17
18impl BuildxRmResult {
19    /// Parse the buildx rm output
20    fn parse(names: &[String], output: &CommandOutput) -> Self {
21        Self {
22            names: names.to_vec(),
23            output: output.stdout.clone(),
24            success: output.success,
25        }
26    }
27}
28
29/// Docker buildx rm command builder
30///
31/// Removes one or more builder instances.
32///
33/// # Example
34///
35/// ```rust,no_run
36/// use docker_wrapper::{DockerCommand, BuildxRmCommand};
37///
38/// # #[tokio::main]
39/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
40/// let result = BuildxRmCommand::new("mybuilder")
41///     .force()
42///     .execute()
43///     .await?;
44///
45/// println!("Removed {} builders", result.names.len());
46/// # Ok(())
47/// # }
48/// ```
49#[derive(Debug, Clone)]
50#[allow(clippy::struct_excessive_bools)]
51pub struct BuildxRmCommand {
52    /// The builder names to remove
53    names: Vec<String>,
54    /// Remove all inactive builders
55    all_inactive: bool,
56    /// Do not prompt for confirmation
57    force: bool,
58    /// Keep the `BuildKit` daemon running
59    keep_daemon: bool,
60    /// Keep `BuildKit` state
61    keep_state: bool,
62    /// Command executor
63    pub executor: CommandExecutor,
64}
65
66impl BuildxRmCommand {
67    /// Create a new buildx rm command
68    ///
69    /// # Arguments
70    ///
71    /// * `name` - The name of the builder to remove
72    #[must_use]
73    pub fn new(name: impl Into<String>) -> Self {
74        Self {
75            names: vec![name.into()],
76            all_inactive: false,
77            force: false,
78            keep_daemon: false,
79            keep_state: false,
80            executor: CommandExecutor::new(),
81        }
82    }
83
84    /// Create a new buildx rm command to remove all inactive builders
85    #[must_use]
86    pub fn all_inactive() -> Self {
87        Self {
88            names: Vec::new(),
89            all_inactive: true,
90            force: false,
91            keep_daemon: false,
92            keep_state: false,
93            executor: CommandExecutor::new(),
94        }
95    }
96
97    /// Add another builder to remove
98    #[must_use]
99    pub fn name(mut self, name: impl Into<String>) -> Self {
100        self.names.push(name.into());
101        self
102    }
103
104    /// Do not prompt for confirmation
105    #[must_use]
106    pub fn force(mut self) -> Self {
107        self.force = true;
108        self
109    }
110
111    /// Keep the `BuildKit` daemon running
112    #[must_use]
113    pub fn keep_daemon(mut self) -> Self {
114        self.keep_daemon = true;
115        self
116    }
117
118    /// Keep `BuildKit` state
119    #[must_use]
120    pub fn keep_state(mut self) -> Self {
121        self.keep_state = true;
122        self
123    }
124
125    /// Build the command arguments
126    fn build_args(&self) -> Vec<String> {
127        let mut args = vec!["buildx".to_string(), "rm".to_string()];
128
129        if self.all_inactive {
130            args.push("--all-inactive".to_string());
131        }
132
133        if self.force {
134            args.push("--force".to_string());
135        }
136
137        if self.keep_daemon {
138            args.push("--keep-daemon".to_string());
139        }
140
141        if self.keep_state {
142            args.push("--keep-state".to_string());
143        }
144
145        for name in &self.names {
146            args.push(name.clone());
147        }
148
149        args.extend(self.executor.raw_args.clone());
150
151        args
152    }
153}
154
155#[async_trait]
156impl DockerCommand for BuildxRmCommand {
157    type Output = BuildxRmResult;
158
159    fn get_executor(&self) -> &CommandExecutor {
160        &self.executor
161    }
162
163    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
164        &mut self.executor
165    }
166
167    fn build_command_args(&self) -> Vec<String> {
168        self.build_args()
169    }
170
171    async fn execute(&self) -> Result<Self::Output> {
172        let args = self.build_args();
173        let output = self.execute_command(args).await?;
174        Ok(BuildxRmResult::parse(&self.names, &output))
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_buildx_rm_basic() {
184        let cmd = BuildxRmCommand::new("mybuilder");
185        let args = cmd.build_args();
186        assert_eq!(args, vec!["buildx", "rm", "mybuilder"]);
187    }
188
189    #[test]
190    fn test_buildx_rm_multiple() {
191        let cmd = BuildxRmCommand::new("builder1").name("builder2");
192        let args = cmd.build_args();
193        assert!(args.contains(&"builder1".to_string()));
194        assert!(args.contains(&"builder2".to_string()));
195    }
196
197    #[test]
198    fn test_buildx_rm_all_inactive() {
199        let cmd = BuildxRmCommand::all_inactive();
200        let args = cmd.build_args();
201        assert!(args.contains(&"--all-inactive".to_string()));
202    }
203
204    #[test]
205    fn test_buildx_rm_with_force() {
206        let cmd = BuildxRmCommand::new("mybuilder").force();
207        let args = cmd.build_args();
208        assert!(args.contains(&"--force".to_string()));
209    }
210
211    #[test]
212    fn test_buildx_rm_all_options() {
213        let cmd = BuildxRmCommand::new("mybuilder")
214            .force()
215            .keep_daemon()
216            .keep_state();
217        let args = cmd.build_args();
218        assert!(args.contains(&"--force".to_string()));
219        assert!(args.contains(&"--keep-daemon".to_string()));
220        assert!(args.contains(&"--keep-state".to_string()));
221    }
222}