...
This commit is contained in:
		
							
								
								
									
										30
									
								
								src/bin/herodo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/bin/herodo.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| //! Herodo binary entry point | ||||
| //! | ||||
| //! This is the main entry point for the herodo binary. | ||||
| //! It parses command line arguments and calls into the implementation in the cmd module. | ||||
|  | ||||
| use clap::{App, Arg}; | ||||
|  | ||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // Parse command line arguments | ||||
|     let matches = App::new("herodo") | ||||
|         .version("0.1.0") | ||||
|         .author("SAL Team") | ||||
|         .about("Executes Rhai scripts for SAL") | ||||
|         .arg( | ||||
|             Arg::with_name("path") | ||||
|                 .short("p") | ||||
|                 .long("path") | ||||
|                 .value_name("PATH") | ||||
|                 .help("Path to directory containing Rhai scripts") | ||||
|                 .required(true) | ||||
|                 .takes_value(true), | ||||
|         ) | ||||
|         .get_matches(); | ||||
|  | ||||
|     // Get the script path from arguments | ||||
|     let script_path = matches.value_of("path").unwrap(); | ||||
|      | ||||
|     // Call the run function from the cmd module | ||||
|     sal::cmd::herodo::run(script_path) | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/cmd/herodo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/cmd/herodo.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| //! Herodo - A Rhai script executor for SAL | ||||
| //! | ||||
| //! This binary loads the Rhai engine, registers all SAL modules, | ||||
| //! and executes Rhai scripts from a specified directory in sorted order. | ||||
|  | ||||
| // Removed unused imports | ||||
| use rhai::Engine; | ||||
| use std::error::Error; | ||||
| use std::fs; | ||||
| use std::path::{Path, PathBuf}; | ||||
| use std::process; | ||||
|  | ||||
| /// Run the herodo script executor with the given script path | ||||
| /// | ||||
| /// # Arguments | ||||
| /// | ||||
| /// * `script_path` - Path to the directory containing Rhai scripts | ||||
| /// | ||||
| /// # Returns | ||||
| /// | ||||
| /// Result indicating success or failure | ||||
| pub fn run(script_path: &str) -> Result<(), Box<dyn Error>> { | ||||
|     let script_dir = Path::new(script_path); | ||||
|  | ||||
|     // Check if the directory exists | ||||
|     if !script_dir.exists() || !script_dir.is_dir() { | ||||
|         eprintln!("Error: '{}' is not a valid directory", script_path); | ||||
|         process::exit(1); | ||||
|     } | ||||
|  | ||||
|     // Create a new Rhai engine | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     // Register println function for output | ||||
|     engine.register_fn("println", |s: &str| println!("{}", s)); | ||||
|  | ||||
|     // Register all SAL modules with the engine | ||||
|     crate::rhai::register(&mut engine)?; | ||||
|  | ||||
|     // Find all .rhai files in the directory | ||||
|     let mut script_files: Vec<PathBuf> = fs::read_dir(script_dir)? | ||||
|         .filter_map(Result::ok) | ||||
|         .filter(|entry| { | ||||
|             entry.path().is_file() && | ||||
|             entry.path().extension().map_or(false, |ext| ext == "rhai") | ||||
|         }) | ||||
|         .map(|entry| entry.path()) | ||||
|         .collect(); | ||||
|  | ||||
|     // Sort the script files by name | ||||
|     script_files.sort(); | ||||
|  | ||||
|     if script_files.is_empty() { | ||||
|         println!("No Rhai scripts found in '{}'", script_path); | ||||
|         return Ok(()); | ||||
|     } | ||||
|  | ||||
|     println!("Found {} Rhai scripts to execute:", script_files.len()); | ||||
|      | ||||
|     // Execute each script in sorted order | ||||
|     for script_file in script_files { | ||||
|         println!("\nExecuting: {}", script_file.display()); | ||||
|          | ||||
|         // Read the script content | ||||
|         let script = fs::read_to_string(&script_file)?; | ||||
|          | ||||
|         // Execute the script | ||||
|         match engine.eval::<rhai::Dynamic>(&script) { | ||||
|             Ok(result) => { | ||||
|                 println!("Script executed successfully"); | ||||
|                 if !result.is_unit() { | ||||
|                     println!("Result: {}", result); | ||||
|                 } | ||||
|             }, | ||||
|             Err(err) => { | ||||
|                 eprintln!("Error executing script: {}", err); | ||||
|                 // Continue with the next script instead of stopping | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     println!("\nAll scripts executed"); | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										5
									
								
								src/cmd/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/cmd/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| //! Command-line tools for SAL | ||||
| //! | ||||
| //! This module contains command-line tools built on top of the SAL library. | ||||
|  | ||||
| pub mod herodo; | ||||
| @@ -43,6 +43,7 @@ pub mod redisclient; | ||||
| pub mod text; | ||||
| pub mod virt; | ||||
| pub mod rhai; | ||||
| pub mod cmd; | ||||
|  | ||||
| // Version information | ||||
| /// Returns the version of the SAL library | ||||
|   | ||||
							
								
								
									
										391
									
								
								src/rhai/buildah.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								src/rhai/buildah.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,391 @@ | ||||
| //! Rhai wrappers for Buildah module functions | ||||
| //! | ||||
| //! This module provides Rhai wrappers for the functions in the Buildah module. | ||||
|  | ||||
| use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; | ||||
| use std::collections::HashMap; | ||||
| use crate::virt::buildah::{self, BuildahError, Image}; | ||||
| use crate::process::CommandResult; | ||||
|  | ||||
| /// Register Buildah module functions with the Rhai engine | ||||
| /// | ||||
| /// # Arguments | ||||
| /// | ||||
| /// * `engine` - The Rhai engine to register the functions with | ||||
| /// | ||||
| /// # Returns | ||||
| /// | ||||
| /// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise | ||||
| pub fn register_buildah_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> { | ||||
|     // Register types | ||||
|     register_buildah_types(engine)?; | ||||
|      | ||||
|     // Register container functions | ||||
|     engine.register_fn("buildah_from", from); | ||||
|     engine.register_fn("buildah_run", run); | ||||
|     engine.register_fn("buildah_run_with_isolation", run_with_isolation); | ||||
|     engine.register_fn("buildah_copy", copy); | ||||
|     engine.register_fn("buildah_add", add); | ||||
|     engine.register_fn("buildah_commit", commit); | ||||
|     engine.register_fn("buildah_remove", remove); | ||||
|     engine.register_fn("buildah_list", list); | ||||
|     engine.register_fn("buildah_build", build_with_options); | ||||
|     engine.register_fn("buildah_new_build_options", new_build_options); | ||||
|      | ||||
|     // Register image functions | ||||
|     engine.register_fn("buildah_images", images); | ||||
|     engine.register_fn("buildah_image_remove", image_remove); | ||||
|     engine.register_fn("buildah_image_push", image_push); | ||||
|     engine.register_fn("buildah_image_tag", image_tag); | ||||
|     engine.register_fn("buildah_image_pull", image_pull); | ||||
|     engine.register_fn("buildah_image_commit", image_commit_with_options); | ||||
|     engine.register_fn("buildah_new_commit_options", new_commit_options); | ||||
|     engine.register_fn("buildah_config", config_with_options); | ||||
|     engine.register_fn("buildah_new_config_options", new_config_options); | ||||
|      | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| /// Register Buildah module types with the Rhai engine | ||||
| fn register_buildah_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> { | ||||
|     // Register Image type and methods | ||||
|     engine.register_type_with_name::<Image>("BuildahImage"); | ||||
|      | ||||
|     // Register getters for Image properties | ||||
|     engine.register_get("id", |img: &mut Image| img.id.clone()); | ||||
|     engine.register_get("names", |img: &mut Image| { | ||||
|         let mut array = Array::new(); | ||||
|         for name in &img.names { | ||||
|             array.push(Dynamic::from(name.clone())); | ||||
|         } | ||||
|         array | ||||
|     }); | ||||
|     engine.register_get("size", |img: &mut Image| img.size.clone()); | ||||
|     engine.register_get("created", |img: &mut Image| img.created.clone()); | ||||
|      | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| // Helper functions for error conversion | ||||
| fn buildah_error_to_rhai_error<T>(result: Result<T, BuildahError>) -> Result<T, Box<EvalAltResult>> { | ||||
|     result.map_err(|e| { | ||||
|         Box::new(EvalAltResult::ErrorRuntime( | ||||
|             format!("Buildah error: {}", e).into(), | ||||
|             rhai::Position::NONE | ||||
|         )) | ||||
|     }) | ||||
| } | ||||
|  | ||||
| /// Create a new Map with default build options | ||||
| pub fn new_build_options() -> Map { | ||||
|     let mut map = Map::new(); | ||||
|     map.insert("tag".into(), Dynamic::UNIT); | ||||
|     map.insert("context_dir".into(), Dynamic::from(".")); | ||||
|     map.insert("file".into(), Dynamic::from("Dockerfile")); | ||||
|     map.insert("isolation".into(), Dynamic::UNIT); | ||||
|     map | ||||
| } | ||||
|  | ||||
| /// Create a new Map with default commit options | ||||
| pub fn new_commit_options() -> Map { | ||||
|     let mut map = Map::new(); | ||||
|     map.insert("format".into(), Dynamic::UNIT); | ||||
|     map.insert("squash".into(), Dynamic::from(false)); | ||||
|     map.insert("rm".into(), Dynamic::from(false)); | ||||
|     map | ||||
| } | ||||
|  | ||||
| /// Create a new Map for config options | ||||
| pub fn new_config_options() -> Map { | ||||
|     Map::new() | ||||
| } | ||||
|  | ||||
| // | ||||
| // Container Function Wrappers | ||||
| // | ||||
|  | ||||
| /// Wrapper for buildah::from | ||||
| /// | ||||
| /// Create a container from an image. | ||||
| pub fn from(image: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::from(image)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::run | ||||
| /// | ||||
| /// Run a command in a container. | ||||
| pub fn run(container: &str, command: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::run(container, command)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::run_with_isolation | ||||
| /// | ||||
| /// Run a command in a container with specified isolation. | ||||
| pub fn run_with_isolation(container: &str, command: &str, isolation: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::run_with_isolation(container, command, isolation)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::copy | ||||
| /// | ||||
| /// Copy files into a container. | ||||
| pub fn copy(container: &str, source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::copy(container, source, dest)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::add | ||||
| /// | ||||
| /// Add files into a container. | ||||
| pub fn add(container: &str, source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::add(container, source, dest)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::commit | ||||
| /// | ||||
| /// Commit a container to an image. | ||||
| pub fn commit(container: &str, image_name: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::commit(container, image_name)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::remove | ||||
| /// | ||||
| /// Remove a container. | ||||
| pub fn remove(container: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::remove(container)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::list | ||||
| /// | ||||
| /// List containers. | ||||
| pub fn list() -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::list()) | ||||
| } | ||||
|  | ||||
| /// Build an image with options specified in a Map | ||||
| /// | ||||
| /// This provides a builder-style interface for Rhai scripts. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```rhai | ||||
| /// let options = buildah_new_build_options(); | ||||
| /// options.tag = "my-image:latest"; | ||||
| /// options.context_dir = "."; | ||||
| /// options.file = "Dockerfile"; | ||||
| /// options.isolation = "chroot"; | ||||
| /// let result = buildah_build(options); | ||||
| /// ``` | ||||
| pub fn build_with_options(options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     // Extract options from the map | ||||
|     let tag_option = match options.get("tag") { | ||||
|         Some(tag) => { | ||||
|             if tag.is_unit() { | ||||
|                 None | ||||
|             } else if let Ok(tag_str) = tag.clone().into_string() { | ||||
|                 Some(tag_str) | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "tag must be a string".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => None | ||||
|     }; | ||||
|      | ||||
|     let context_dir = match options.get("context_dir") { | ||||
|         Some(dir) => { | ||||
|             if let Ok(dir_str) = dir.clone().into_string() { | ||||
|                 dir_str | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "context_dir must be a string".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => String::from(".") | ||||
|     }; | ||||
|      | ||||
|     let file = match options.get("file") { | ||||
|         Some(file) => { | ||||
|             if let Ok(file_str) = file.clone().into_string() { | ||||
|                 file_str | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "file must be a string".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => String::from("Dockerfile") | ||||
|     }; | ||||
|      | ||||
|     let isolation_option = match options.get("isolation") { | ||||
|         Some(isolation) => { | ||||
|             if isolation.is_unit() { | ||||
|                 None | ||||
|             } else if let Ok(isolation_str) = isolation.clone().into_string() { | ||||
|                 Some(isolation_str) | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "isolation must be a string".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => None | ||||
|     }; | ||||
|      | ||||
|     // Convert String to &str for the function call | ||||
|     let tag_ref = tag_option.as_deref(); | ||||
|     let isolation_ref = isolation_option.as_deref(); | ||||
|      | ||||
|     // Call the buildah build function | ||||
|     buildah_error_to_rhai_error(buildah::build(tag_ref, &context_dir, &file, isolation_ref)) | ||||
| } | ||||
|  | ||||
| // | ||||
| // Image Function Wrappers | ||||
| // | ||||
|  | ||||
| /// Wrapper for buildah::images | ||||
| /// | ||||
| /// List images in local storage. | ||||
| pub fn images() -> Result<Array, Box<EvalAltResult>> { | ||||
|     let images = buildah_error_to_rhai_error(buildah::images())?; | ||||
|      | ||||
|     // Convert Vec<Image> to Rhai Array | ||||
|     let mut array = Array::new(); | ||||
|     for image in images { | ||||
|         array.push(Dynamic::from(image)); | ||||
|     } | ||||
|      | ||||
|     Ok(array) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::image_remove | ||||
| /// | ||||
| /// Remove one or more images. | ||||
| pub fn image_remove(image: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::image_remove(image)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::image_push | ||||
| /// | ||||
| /// Push an image to a registry. | ||||
| pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::image_push(image, destination, tls_verify)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::image_tag | ||||
| /// | ||||
| /// Add an additional name to a local image. | ||||
| pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::image_tag(image, new_name)) | ||||
| } | ||||
|  | ||||
| /// Wrapper for buildah::image_pull | ||||
| /// | ||||
| /// Pull an image from a registry. | ||||
| pub fn image_pull(image: &str, tls_verify: bool) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     buildah_error_to_rhai_error(buildah::image_pull(image, tls_verify)) | ||||
| } | ||||
|  | ||||
| /// Commit a container to an image with options specified in a Map | ||||
| /// | ||||
| /// This provides a builder-style interface for Rhai scripts. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```rhai | ||||
| /// let options = buildah_new_commit_options(); | ||||
| /// options.format = "docker"; | ||||
| /// options.squash = true; | ||||
| /// options.rm = true; | ||||
| /// let result = buildah_image_commit("my-container", "my-image:latest", options); | ||||
| /// ``` | ||||
| pub fn image_commit_with_options(container: &str, image_name: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     // Extract options from the map | ||||
|     let format_option = match options.get("format") { | ||||
|         Some(format) => { | ||||
|             if format.is_unit() { | ||||
|                 None | ||||
|             } else if let Ok(format_str) = format.clone().into_string() { | ||||
|                 Some(format_str) | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "format must be a string".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => None | ||||
|     }; | ||||
|      | ||||
|     let squash = match options.get("squash") { | ||||
|         Some(squash) => { | ||||
|             if let Ok(squash_val) = squash.clone().as_bool() { | ||||
|                 squash_val | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "squash must be a boolean".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => false | ||||
|     }; | ||||
|      | ||||
|     let rm = match options.get("rm") { | ||||
|         Some(rm) => { | ||||
|             if let Ok(rm_val) = rm.clone().as_bool() { | ||||
|                 rm_val | ||||
|             } else { | ||||
|                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                     "rm must be a boolean".into(), | ||||
|                     rhai::Position::NONE | ||||
|                 ))); | ||||
|             } | ||||
|         }, | ||||
|         None => false | ||||
|     }; | ||||
|      | ||||
|     // Convert String to &str for the function call | ||||
|     let format_ref = format_option.as_deref(); | ||||
|      | ||||
|     // Call the buildah image_commit function | ||||
|     buildah_error_to_rhai_error(buildah::image_commit(container, image_name, format_ref, squash, rm)) | ||||
| } | ||||
|  | ||||
| /// Configure a container with options specified in a Map | ||||
| /// | ||||
| /// This provides a builder-style interface for Rhai scripts. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```rhai | ||||
| /// let options = buildah_new_config_options(); | ||||
| /// options.author = "John Doe"; | ||||
| /// options.cmd = "echo Hello"; | ||||
| /// options.entrypoint = "/bin/sh -c"; | ||||
| /// let result = buildah_config("my-container", options); | ||||
| /// ``` | ||||
| pub fn config_with_options(container: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     // Convert Rhai Map to Rust HashMap | ||||
|     let mut config_options = HashMap::<String, String>::new(); | ||||
|      | ||||
|     for (key, value) in options.iter() { | ||||
|         if let Ok(value_str) = value.clone().into_string() { | ||||
|             // Convert SmartString to String | ||||
|             config_options.insert(key.to_string(), value_str); | ||||
|         } else { | ||||
|             return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||
|                 format!("Option '{}' must be a string", key).into(), | ||||
|                 rhai::Position::NONE | ||||
|             ))); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // Call the buildah config function | ||||
|     buildah_error_to_rhai_error(buildah::config(container, config_options)) | ||||
| } | ||||
| @@ -6,15 +6,49 @@ | ||||
| mod error; | ||||
| mod os; | ||||
| mod process; | ||||
| mod buildah; | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests; | ||||
|  | ||||
| use rhai::Engine; | ||||
| // Re-export common Rhai types for convenience | ||||
| pub use rhai::{Array, Dynamic, Map, EvalAltResult, Engine}; | ||||
|  | ||||
| // Re-export error module | ||||
| pub use error::*; | ||||
| pub use os::*; | ||||
| pub use process::*; | ||||
|  | ||||
| // Re-export specific functions from modules to avoid name conflicts | ||||
| pub use os::{ | ||||
|     register_os_module, | ||||
|     // File system functions | ||||
|     exist, find_file, find_files, find_dir, find_dirs,  | ||||
|     delete, mkdir, file_size, rsync, | ||||
|     // Download functions | ||||
|     download, download_install | ||||
| }; | ||||
|  | ||||
| pub use process::{ | ||||
|     register_process_module, | ||||
|     // Run functions | ||||
|     run_command, run_silent, run_with_options, new_run_options, | ||||
|     // Process management functions | ||||
|     which, kill, process_list, process_get | ||||
| }; | ||||
|  | ||||
| pub use buildah::{ | ||||
|     register_buildah_module, | ||||
|     // Container functions | ||||
|     from, run, run_with_isolation, add, commit, remove, list, | ||||
|     build_with_options, new_build_options, | ||||
|     // Image functions | ||||
|     images, image_remove, image_push, image_tag, image_pull, | ||||
|     image_commit_with_options, new_commit_options, | ||||
|     config_with_options, new_config_options | ||||
| }; | ||||
|  | ||||
| // Rename copy functions to avoid conflicts | ||||
| pub use os::copy as os_copy; | ||||
| pub use buildah::copy as buildah_copy; | ||||
|  | ||||
| /// Register all SAL modules with the Rhai engine | ||||
| /// | ||||
| @@ -41,6 +75,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     // Register Process module functions | ||||
|     process::register_process_module(engine)?; | ||||
|      | ||||
|     // Register Buildah module functions | ||||
|     buildah::register_buildah_module(engine)?; | ||||
|      | ||||
|     // Future modules can be registered here | ||||
|      | ||||
|     Ok(()) | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| //! | ||||
| //! This module provides Rhai wrappers for the functions in the Process module. | ||||
|  | ||||
| use rhai::{Engine, EvalAltResult, Array, Dynamic}; | ||||
| use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; | ||||
| use crate::process::{self, CommandResult, ProcessInfo, RunError, ProcessError}; | ||||
|  | ||||
| /// Register Process module functions with the Rhai engine | ||||
| @@ -21,6 +21,8 @@ pub fn register_process_module(engine: &mut Engine) -> Result<(), Box<EvalAltRes | ||||
|     // Register run functions | ||||
|     engine.register_fn("run_command", run_command); | ||||
|     engine.register_fn("run_silent", run_silent); | ||||
|     engine.register_fn("run", run_with_options); | ||||
|     engine.register_fn("new_run_options", new_run_options); | ||||
|      | ||||
|     // Register process management functions | ||||
|     engine.register_fn("which", which); | ||||
| @@ -51,9 +53,6 @@ fn register_process_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> | ||||
|     engine.register_get("memory", |p: &mut ProcessInfo| p.memory); | ||||
|     engine.register_get("cpu", |p: &mut ProcessInfo| p.cpu); | ||||
|      | ||||
|     // Register error conversion functions | ||||
|     engine.register_fn("to_string", |err: &str| err.to_string()); | ||||
|      | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @@ -76,6 +75,16 @@ fn process_error_to_rhai_error<T>(result: Result<T, ProcessError>) -> Result<T, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| /// Create a new Map with default run options | ||||
| pub fn new_run_options() -> Map { | ||||
|     let mut map = Map::new(); | ||||
|     map.insert("die".into(), Dynamic::from(true)); | ||||
|     map.insert("silent".into(), Dynamic::from(false)); | ||||
|     map.insert("async_exec".into(), Dynamic::from(false)); | ||||
|     map.insert("log".into(), Dynamic::from(false)); | ||||
|     map | ||||
| } | ||||
|  | ||||
| // | ||||
| // Run Function Wrappers | ||||
| // | ||||
| @@ -94,6 +103,50 @@ pub fn run_silent(command: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     run_error_to_rhai_error(process::run_silent(command)) | ||||
| } | ||||
|  | ||||
| /// Run a command with options specified in a Map | ||||
| /// | ||||
| /// This provides a builder-style interface for Rhai scripts. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```rhai | ||||
| /// let options = new_run_options(); | ||||
| /// options.die = false; | ||||
| /// options.silent = true; | ||||
| /// let result = run("echo Hello", options); | ||||
| /// ``` | ||||
| pub fn run_with_options(command: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||
|     let mut builder = process::run(command); | ||||
|      | ||||
|     // Apply options from the map | ||||
|     if let Some(die) = options.get("die") { | ||||
|         if let Ok(die_val) = die.clone().as_bool() { | ||||
|             builder = builder.die(die_val); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     if let Some(silent) = options.get("silent") { | ||||
|         if let Ok(silent_val) = silent.clone().as_bool() { | ||||
|             builder = builder.silent(silent_val); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     if let Some(async_exec) = options.get("async_exec") { | ||||
|         if let Ok(async_val) = async_exec.clone().as_bool() { | ||||
|             builder = builder.async_exec(async_val); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     if let Some(log) = options.get("log") { | ||||
|         if let Ok(log_val) = log.clone().as_bool() { | ||||
|             builder = builder.log(log_val); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // Execute the command | ||||
|     run_error_to_rhai_error(builder.execute()) | ||||
| } | ||||
|  | ||||
| // | ||||
| // Process Management Function Wrappers | ||||
| // | ||||
|   | ||||
| @@ -117,6 +117,36 @@ mod tests { | ||||
|         assert_eq!(result, ()); | ||||
|     } | ||||
|      | ||||
|     #[test] | ||||
|     fn test_run_with_options() { | ||||
|         let mut engine = Engine::new(); | ||||
|         register(&mut engine).unwrap(); | ||||
|          | ||||
|         // Test running a command with custom options | ||||
|         #[cfg(target_os = "windows")] | ||||
|         let script = r#" | ||||
|             let options = new_run_options(); | ||||
|             options["die"] = true; | ||||
|             options["silent"] = false; | ||||
|             options["log"] = true; | ||||
|             let result = run("echo Hello World", options); | ||||
|             result.success && result.stdout.contains("Hello World") | ||||
|         "#; | ||||
|          | ||||
|         #[cfg(any(target_os = "macos", target_os = "linux"))] | ||||
|         let script = r#" | ||||
|             let options = new_run_options(); | ||||
|             options["die"] = true; | ||||
|             options["silent"] = false; | ||||
|             options["log"] = true; | ||||
|             let result = run("echo 'Hello World'", options); | ||||
|             result.success && result.stdout.contains("Hello World") | ||||
|         "#; | ||||
|          | ||||
|         let result = engine.eval::<bool>(script).unwrap(); | ||||
|         assert!(result); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_run_command() { | ||||
|         let mut engine = Engine::new(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user