docker_wrapper/command/
load.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::Result;
7use async_trait::async_trait;
8use std::path::Path;
9
10#[derive(Debug, Clone)]
32pub struct LoadCommand {
33 input: Option<String>,
35 quiet: bool,
37 pub executor: CommandExecutor,
39}
40
41impl LoadCommand {
42 #[must_use]
52 pub fn new() -> Self {
53 Self {
54 input: None,
55 quiet: false,
56 executor: CommandExecutor::new(),
57 }
58 }
59
60 #[must_use]
72 pub fn input(mut self, path: &Path) -> Self {
73 self.input = Some(path.to_string_lossy().into_owned());
74 self
75 }
76
77 #[must_use]
87 pub fn quiet(mut self) -> Self {
88 self.quiet = true;
89 self
90 }
91
92 pub async fn run(&self) -> Result<LoadResult> {
122 let output = self.execute().await?;
123
124 let loaded_images = Self::parse_loaded_images(&output.stdout);
126
127 Ok(LoadResult {
128 output,
129 loaded_images,
130 })
131 }
132
133 fn parse_loaded_images(stdout: &str) -> Vec<String> {
135 let mut images = Vec::new();
136 for line in stdout.lines() {
137 if line.starts_with("Loaded image:") {
138 if let Some(image) = line.strip_prefix("Loaded image:") {
139 images.push(image.trim().to_string());
140 }
141 } else if line.starts_with("Loaded image ID:") {
142 if let Some(id) = line.strip_prefix("Loaded image ID:") {
143 images.push(id.trim().to_string());
144 }
145 }
146 }
147 images
148 }
149}
150
151impl Default for LoadCommand {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156
157#[async_trait]
158impl DockerCommand for LoadCommand {
159 type Output = CommandOutput;
160
161 fn build_command_args(&self) -> Vec<String> {
162 let mut args = vec!["load".to_string()];
163
164 if let Some(ref input_file) = self.input {
165 args.push("--input".to_string());
166 args.push(input_file.clone());
167 }
168
169 if self.quiet {
170 args.push("--quiet".to_string());
171 }
172
173 args.extend(self.executor.raw_args.clone());
174 args
175 }
176
177 fn get_executor(&self) -> &CommandExecutor {
178 &self.executor
179 }
180
181 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
182 &mut self.executor
183 }
184
185 async fn execute(&self) -> Result<Self::Output> {
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 LoadResult {
198 pub output: CommandOutput,
200 pub loaded_images: Vec<String>,
202}
203
204impl LoadResult {
205 #[must_use]
207 pub fn success(&self) -> bool {
208 self.output.success
209 }
210
211 #[must_use]
213 pub fn loaded_images(&self) -> &[String] {
214 &self.loaded_images
215 }
216
217 #[must_use]
219 pub fn image_count(&self) -> usize {
220 self.loaded_images.len()
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_load_basic() {
230 let cmd = LoadCommand::new();
231 let args = cmd.build_command_args();
232 assert_eq!(args, vec!["load"]);
233 }
234
235 #[test]
236 fn test_load_with_input() {
237 let cmd = LoadCommand::new().input(Path::new("images.tar"));
238 let args = cmd.build_command_args();
239 assert_eq!(args, vec!["load", "--input", "images.tar"]);
240 }
241
242 #[test]
243 fn test_load_with_quiet() {
244 let cmd = LoadCommand::new().quiet();
245 let args = cmd.build_command_args();
246 assert_eq!(args, vec!["load", "--quiet"]);
247 }
248
249 #[test]
250 fn test_load_with_all_options() {
251 let cmd = LoadCommand::new()
252 .input(Path::new("/tmp/alpine.tar"))
253 .quiet();
254 let args = cmd.build_command_args();
255 assert_eq!(args, vec!["load", "--input", "/tmp/alpine.tar", "--quiet"]);
256 }
257
258 #[test]
259 fn test_parse_loaded_images() {
260 let output =
261 "Loaded image: alpine:latest\nLoaded image: nginx:1.21\nLoaded image ID: sha256:abc123";
262 let images = LoadCommand::parse_loaded_images(output);
263 assert_eq!(images, vec!["alpine:latest", "nginx:1.21", "sha256:abc123"]);
264 }
265
266 #[test]
267 fn test_parse_loaded_images_empty() {
268 let output = "";
269 let images = LoadCommand::parse_loaded_images(output);
270 assert!(images.is_empty());
271 }
272}