diff --git a/src/virt/buildah/cmd.rs b/src/virt/buildah/cmd.rs index 0214697..29884e8 100644 --- a/src/virt/buildah/cmd.rs +++ b/src/virt/buildah/cmd.rs @@ -1,9 +1,11 @@ // Basic buildah operations for container management use std::process::Command; +use crate::process::CommandResult; +use super::BuildahError; /// Execute a buildah command and return the result -pub fn execute_buildah_command(args: &[&str]) -> Dynamic { +pub fn execute_buildah_command(args: &[&str]) -> Result { let output = Command::new("buildah") .args(args) .output(); @@ -20,10 +22,15 @@ pub fn execute_buildah_command(args: &[&str]) -> Dynamic { code: output.status.code().unwrap_or(-1), }; - result.to_dynamic() + if result.success { + Ok(result) + } else { + Err(BuildahError::CommandFailed(format!("Command failed with code {}: {}", + result.code, result.stderr.trim()))) + } }, Err(e) => { - CommandResult::error(&format!("Failed to execute buildah command: {}", e)).to_dynamic() + Err(BuildahError::CommandExecutionFailed(e)) } } } diff --git a/src/virt/buildah/containers.rs b/src/virt/buildah/containers.rs index e2aa0b5..cf217c2 100644 --- a/src/virt/buildah/containers.rs +++ b/src/virt/buildah/containers.rs @@ -1,37 +1,38 @@ - use crate::virt::buildah::execute_buildah_command; +use crate::process::CommandResult; +use super::BuildahError; /// Create a container from an image -pub fn build_from(image: &str) -> Dynamic { +pub fn from(image: &str) -> Result { execute_buildah_command(&["from", image]) } /// Run a command in a container -pub fn build_run(container: &str, command: &str) -> Dynamic { +pub fn run(container: &str, command: &str) -> Result { execute_buildah_command(&["run", container, "sh", "-c", command]) } /// Copy files into a container -pub fn build_copy(container: &str, source: &str, dest: &str) -> Dynamic { +pub fn copy(container: &str, source: &str, dest: &str) -> Result { execute_buildah_command(&["copy", container, source, dest]) } -pub fn build_add(container: &str, source: &str, dest: &str) -> Dynamic { +pub fn add(container: &str, source: &str, dest: &str) -> Result { execute_buildah_command(&["add", container, source, dest]) } /// Commit a container to an image -pub fn build_commit(container: &str, image_name: &str) -> Dynamic { +pub fn commit(container: &str, image_name: &str) -> Result { execute_buildah_command(&["commit", container, image_name]) } /// Remove a container -pub fn build_remove(container: &str) -> Dynamic { +pub fn remove(container: &str) -> Result { execute_buildah_command(&["rm", container]) } /// List containers -pub fn build_list() -> Dynamic { +pub fn list() -> Result { execute_buildah_command(&["containers"]) } diff --git a/src/virt/buildah/images.rs b/src/virt/buildah/images.rs index ddf660f..c9f00d3 100644 --- a/src/virt/buildah/images.rs +++ b/src/virt/buildah/images.rs @@ -1,103 +1,98 @@ use std::process::Command; +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 -/// * Array of image details on success or error details -pub fn images() -> Dynamic { - let result = execute_buildah_command(&["images", "--format", "json"]); +/// * Result with array of Image objects on success or error details +pub fn images() -> Result, BuildahError> { + let result = execute_buildah_command(&["images", "--format", "json"])?; - let result_map = match result.clone().try_cast::() { - Some(map) => map, - None => { - return create_result("", "Failed to convert result to map", false, -1); - } - }; - - let success = match result_map.get("success") { - Some(val) => val.as_bool().unwrap_or(false), - None => false, - }; - - if success { - let output = match result_map.get("stdout") { - Some(val) => val.to_string(), - None => "".to_string(), - }; - - // Try to parse the JSON output - match serde_json::from_str::(&output) { - Ok(json) => { - if let serde_json::Value::Array(images) = json { - let mut image_array = Array::new(); + // 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())), + }; - for image in images { - let mut image_map = Map::new(); - - if let Some(id) = image.get("id").and_then(|v| v.as_str()) { - image_map.insert("id".into(), Dynamic::from(id.to_string())); - } - - if let Some(name) = image.get("names").and_then(|v| v.as_array()) { - let mut names_array = Array::new(); - for name_value in name { + // 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_array.push(Dynamic::from(name_str.to_string())); + names_vec.push(name_str.to_string()); } } - image_map.insert("names".into(), Dynamic::from(names_array)); - } - - if let Some(size) = image.get("size").and_then(|v| v.as_str()) { - image_map.insert("size".into(), Dynamic::from(size.to_string())); - } - - if let Some(created) = image.get("created").and_then(|v| v.as_str()) { - image_map.insert("created".into(), Dynamic::from(created.to_string())); - } - - image_array.push(Dynamic::from(image_map)); - } - - let mut result_map = match result.clone().try_cast::() { - Some(map) => map, - None => Map::new(), + names_vec + }, + None => Vec::new(), // Empty vector if no names found }; - result_map.insert("images".into(), Dynamic::from(image_array)); - Dynamic::from(result_map) - } else { - create_result( - "", - "Failed to parse image list: Expected JSON array", - false, - -1 - ) + + // 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, + }); } - }, - Err(e) => { - create_result( - "", - &format!("Failed to parse image list JSON: {}", e), - false, - -1 - ) + + Ok(images) + } else { + Err(BuildahError::JsonParseError("Expected JSON array".to_string())) } + }, + Err(e) => { + Err(BuildahError::JsonParseError(format!("Failed to parse image list JSON: {}", e))) } - } else { - result } } - /// Remove one or more images /// /// # Arguments /// * `image` - Image ID or name /// /// # Returns -/// * Success or error details -pub fn image_remove(image: &str) -> Dynamic { +/// * Result with command output or error +pub fn image_remove(image: &str) -> Result { execute_buildah_command(&["rmi", image]) } @@ -109,8 +104,8 @@ pub fn image_remove(image: &str) -> Dynamic { /// * `tls_verify` - Whether to verify TLS (default: true) /// /// # Returns -/// * Success or error details -pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Dynamic { +/// * 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 { @@ -130,8 +125,8 @@ pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Dynamic { /// * `new_name` - New name for the image /// /// # Returns -/// * Success or error details -pub fn image_tag(image: &str, new_name: &str) -> Dynamic { +/// * Result with command output or error +pub fn image_tag(image: &str, new_name: &str) -> Result { execute_buildah_command(&["tag", image, new_name]) } @@ -142,8 +137,8 @@ pub fn image_tag(image: &str, new_name: &str) -> Dynamic { /// * `tls_verify` - Whether to verify TLS (default: true) /// /// # Returns -/// * Success or error details -pub fn image_pull(image: &str, tls_verify: bool) -> Dynamic { +/// * Result with command output or error +pub fn image_pull(image: &str, tls_verify: bool) -> Result { let mut args = vec!["pull"]; if !tls_verify { @@ -165,8 +160,8 @@ pub fn image_pull(image: &str, tls_verify: bool) -> Dynamic { /// * `rm` - Whether to remove the container after commit /// /// # Returns -/// * Success or error details -pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Dynamic { +/// * 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 { @@ -195,8 +190,8 @@ pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squ /// * `options` - Map of configuration options /// /// # Returns -/// * Success or error details -pub fn config(container: &str, options: Map) -> Dynamic { +/// * 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()); @@ -204,10 +199,7 @@ pub fn config(container: &str, options: Map) -> Dynamic { for (key, value) in options.iter() { let option_name = format!("--{}", key); args_owned.push(option_name); - - if !value.is_unit() { - args_owned.push(value.to_string()); - } + args_owned.push(value.clone()); } args_owned.push(container.to_string()); @@ -217,4 +209,3 @@ pub fn config(container: &str, options: Map) -> Dynamic { execute_buildah_command(&args) } - diff --git a/src/virt/buildah/mod.rs b/src/virt/buildah/mod.rs index cc84555..667081a 100644 --- a/src/virt/buildah/mod.rs +++ b/src/virt/buildah/mod.rs @@ -2,6 +2,46 @@ mod containers; mod images; mod cmd; +use std::fmt; +use std::error::Error; +use std::io; + +/// Error type for buildah operations +#[derive(Debug)] +pub enum BuildahError { + /// The buildah command failed to execute + CommandExecutionFailed(io::Error), + /// The buildah command executed but returned an error + CommandFailed(String), + /// Failed to parse JSON output + JsonParseError(String), + /// Failed to convert data + ConversionError(String), + /// Generic error + Other(String), +} + +impl fmt::Display for BuildahError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BuildahError::CommandExecutionFailed(e) => write!(f, "Failed to execute buildah command: {}", e), + BuildahError::CommandFailed(e) => write!(f, "Buildah command failed: {}", e), + BuildahError::JsonParseError(e) => write!(f, "Failed to parse JSON: {}", e), + BuildahError::ConversionError(e) => write!(f, "Conversion error: {}", e), + BuildahError::Other(e) => write!(f, "{}", e), + } + } +} + +impl Error for BuildahError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + BuildahError::CommandExecutionFailed(e) => Some(e), + _ => None, + } + } +} + pub use containers::*; pub use images::*; pub use cmd::*;