use std::collections::HashMap; use crate::virt::buildah::execute_buildah_command; use crate::process::CommandResult; use super::BuildahError; use serde_json::{self, Value}; use serde::{Deserialize, Serialize}; /// Represents a container image #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Image { /// Image ID pub id: String, /// Image names/tags pub names: Vec, /// Image size pub size: String, /// Creation timestamp pub created: String, } /// List images in local storage /// /// # Returns /// * Result with array of Image objects on success or error details pub fn images() -> Result, BuildahError> { let result = execute_buildah_command(&["images", "--format", "json"])?; // Try to parse the JSON output match serde_json::from_str::(&result.stdout) { Ok(json) => { if let Value::Array(images_json) = json { let mut images = Vec::new(); for image_json in images_json { // Extract image ID let id = match image_json.get("id").and_then(|v| v.as_str()) { Some(id) => id.to_string(), None => return Err(BuildahError::ConversionError("Missing image ID".to_string())), }; // Extract image names let names = match image_json.get("names").and_then(|v| v.as_array()) { Some(names_array) => { let mut names_vec = Vec::new(); for name_value in names_array { if let Some(name_str) = name_value.as_str() { names_vec.push(name_str.to_string()); } } names_vec }, None => Vec::new(), // Empty vector if no names found }; // Extract image size let size = match image_json.get("size").and_then(|v| v.as_str()) { Some(size) => size.to_string(), None => "Unknown".to_string(), // Default value if size not found }; // Extract creation timestamp let created = match image_json.get("created").and_then(|v| v.as_str()) { Some(created) => created.to_string(), None => "Unknown".to_string(), // Default value if created not found }; // Create Image struct and add to vector images.push(Image { id, names, size, created, }); } Ok(images) } else { Err(BuildahError::JsonParseError("Expected JSON array".to_string())) } }, Err(e) => { Err(BuildahError::JsonParseError(format!("Failed to parse image list JSON: {}", e))) } } } /// Remove one or more images /// /// # Arguments /// * `image` - Image ID or name /// /// # Returns /// * Result with command output or error pub fn image_remove(image: &str) -> Result { execute_buildah_command(&["rmi", image]) } /// Push an image to a registry /// /// # Arguments /// * `image` - Image name /// * `destination` - Destination (e.g., "docker://registry.example.com/myimage:latest") /// * `tls_verify` - Whether to verify TLS (default: true) /// /// # Returns /// * Result with command output or error pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Result { let mut args = vec!["push"]; if !tls_verify { args.push("--tls-verify=false"); } args.push(image); args.push(destination); execute_buildah_command(&args) } /// Add an additional name to a local image /// /// # Arguments /// * `image` - Image ID or name /// * `new_name` - New name for the image /// /// # Returns /// * Result with command output or error pub fn image_tag(image: &str, new_name: &str) -> Result { execute_buildah_command(&["tag", image, new_name]) } /// Pull an image from a registry /// /// # Arguments /// * `image` - Image name /// * `tls_verify` - Whether to verify TLS (default: true) /// /// # Returns /// * Result with command output or error pub fn image_pull(image: &str, tls_verify: bool) -> Result { let mut args = vec!["pull"]; if !tls_verify { args.push("--tls-verify=false"); } args.push(image); execute_buildah_command(&args) } /// Commit a container to an image /// /// # Arguments /// * `container` - Container ID or name /// * `image_name` - New name for the image /// * `format` - Optional, format to use for the image (oci or docker) /// * `squash` - Whether to squash layers /// * `rm` - Whether to remove the container after commit /// /// # Returns /// * Result with command output or error pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Result { let mut args = vec!["commit"]; if let Some(format_str) = format { args.push("--format"); args.push(format_str); } if squash { args.push("--squash"); } if rm { args.push("--rm"); } args.push(container); args.push(image_name); execute_buildah_command(&args) } /// Container configuration options /// /// # Arguments /// * `container` - Container ID or name /// * `options` - Map of configuration options /// /// # Returns /// * Result with command output or error pub fn config(container: &str, options: HashMap) -> Result { let mut args_owned: Vec = Vec::new(); args_owned.push("config".to_string()); // Process options map for (key, value) in options.iter() { let option_name = format!("--{}", key); args_owned.push(option_name); args_owned.push(value.clone()); } args_owned.push(container.to_string()); // Convert Vec to Vec<&str> for execute_buildah_command let args: Vec<&str> = args_owned.iter().map(|s| s.as_str()).collect(); execute_buildah_command(&args) }