docker_wrapper/command/builder/
ls.rs1use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone)]
9pub struct BuilderInfo {
10 pub name: String,
12 pub driver: Option<String>,
14 pub is_default: bool,
16 pub status: Option<String>,
18}
19
20#[derive(Debug, Clone)]
22pub struct BuildxLsResult {
23 pub builders: Vec<BuilderInfo>,
25 pub output: String,
27 pub success: bool,
29}
30
31impl BuildxLsResult {
32 fn parse(output: &CommandOutput) -> Self {
34 let stdout = &output.stdout;
35 let mut builders = Vec::new();
36
37 for line in stdout.lines().skip(1) {
39 let line = line.trim();
40 if line.is_empty() || line.starts_with(' ') {
41 continue;
42 }
43
44 let parts: Vec<&str> = line.split_whitespace().collect();
49 if parts.is_empty() {
50 continue;
51 }
52
53 let mut name = parts[0].to_string();
54 let is_default = name.ends_with('*') || parts.get(1) == Some(&"*");
55
56 if name.ends_with('*') {
58 name = name.trim_end_matches('*').to_string();
59 }
60
61 let driver = if is_default && parts.len() > 2 {
62 Some(parts[2].to_string())
63 } else if !is_default && parts.len() > 1 {
64 Some(parts[1].to_string())
65 } else {
66 None
67 };
68
69 if name.contains('/') && !name.starts_with('/') {
71 continue;
72 }
73
74 builders.push(BuilderInfo {
75 name,
76 driver,
77 is_default,
78 status: None,
79 });
80 }
81
82 Self {
83 builders,
84 output: stdout.clone(),
85 success: output.success,
86 }
87 }
88}
89
90#[derive(Debug, Clone, Default)]
112pub struct BuildxLsCommand {
113 format: Option<String>,
115 no_trunc: bool,
117 pub executor: CommandExecutor,
119}
120
121impl BuildxLsCommand {
122 #[must_use]
124 pub fn new() -> Self {
125 Self::default()
126 }
127
128 #[must_use]
130 pub fn format(mut self, format: impl Into<String>) -> Self {
131 self.format = Some(format.into());
132 self
133 }
134
135 #[must_use]
137 pub fn no_trunc(mut self) -> Self {
138 self.no_trunc = true;
139 self
140 }
141
142 fn build_args(&self) -> Vec<String> {
144 let mut args = vec!["buildx".to_string(), "ls".to_string()];
145
146 if let Some(ref format) = self.format {
147 args.push("--format".to_string());
148 args.push(format.clone());
149 }
150
151 if self.no_trunc {
152 args.push("--no-trunc".to_string());
153 }
154
155 args.extend(self.executor.raw_args.clone());
156
157 args
158 }
159}
160
161#[async_trait]
162impl DockerCommand for BuildxLsCommand {
163 type Output = BuildxLsResult;
164
165 fn get_executor(&self) -> &CommandExecutor {
166 &self.executor
167 }
168
169 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
170 &mut self.executor
171 }
172
173 fn build_command_args(&self) -> Vec<String> {
174 self.build_args()
175 }
176
177 async fn execute(&self) -> Result<Self::Output> {
178 let args = self.build_args();
179 let output = self.execute_command(args).await?;
180 Ok(BuildxLsResult::parse(&output))
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_buildx_ls_basic() {
190 let cmd = BuildxLsCommand::new();
191 let args = cmd.build_args();
192 assert_eq!(args, vec!["buildx", "ls"]);
193 }
194
195 #[test]
196 fn test_buildx_ls_with_format() {
197 let cmd = BuildxLsCommand::new().format("json");
198 let args = cmd.build_args();
199 assert!(args.contains(&"--format".to_string()));
200 assert!(args.contains(&"json".to_string()));
201 }
202
203 #[test]
204 fn test_buildx_ls_with_no_trunc() {
205 let cmd = BuildxLsCommand::new().no_trunc();
206 let args = cmd.build_args();
207 assert!(args.contains(&"--no-trunc".to_string()));
208 }
209
210 #[test]
211 fn test_buildx_ls_result_parse() {
212 let output = CommandOutput {
213 stdout: "NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS\ndefault * docker\nmybuilder docker-container running v0.12.5 linux/amd64".to_string(),
214 stderr: String::new(),
215 exit_code: 0,
216 success: true,
217 };
218 let result = BuildxLsResult::parse(&output);
219 assert_eq!(result.builders.len(), 2);
220 assert!(result.builders[0].is_default);
221 assert_eq!(result.builders[0].name, "default");
222 assert_eq!(result.builders[1].name, "mybuilder");
223 }
224}