//! Herodo - A Rhai script executor for SAL //! //! This library loads the Rhai engine, registers all SAL modules, //! and executes Rhai scripts from a specified directory in sorted order. use rhai::{Engine, Scope}; 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 a Rhai script file or directory containing Rhai scripts /// /// # Returns /// /// Result indicating success or failure pub fn run(script_path: &str) -> Result<(), Box> { let path = Path::new(script_path); // Check if the path exists if !path.exists() { eprintln!("Error: '{}' does not exist", script_path); process::exit(1); } // Create a new Rhai engine let mut engine = Engine::new(); // TODO: if we create a scope here we could clean up all the different functionsand types regsitered wit the engine // We should generalize the way we add things to the scope for each module sepeartely let mut scope = Scope::new(); // TODO: this should be done for the other clients as well (but not here of course, in each module) let hetzner_client = sal::hetzner::api::Client::new(sal::hetzner::config::Config::from_env().unwrap()); scope.push("hetzner", hetzner_client); // This makes it easy to call e.g. `hetzner.get_server()` or `mycelium.get_connected_peers()` // --> without the need of manually created a client for each one first // --> could be conditionally compiled to only use those who we need (we only push the things to the scope that we actually need to run the script) // Register println function for output engine.register_fn("println", |s: &str| println!("{}", s)); // Register all SAL modules with the engine sal::rhai::register(&mut engine)?; // Collect script files to execute let script_files: Vec = if path.is_file() { // Single file if let Some(extension) = path.extension() { if extension != "rhai" { eprintln!("Warning: '{}' does not have a .rhai extension", script_path); } } vec![path.to_path_buf()] } else if path.is_dir() { // Directory - collect all .rhai files recursively and sort them let mut files = Vec::new(); collect_rhai_files(path, &mut files)?; if files.is_empty() { eprintln!("No .rhai files found in directory: {}", script_path); process::exit(1); } // Sort files for consistent execution order files.sort(); files } else { eprintln!("Error: '{}' is neither a file nor a directory", script_path); process::exit(1); }; println!( "Found {} Rhai script{} to execute:", script_files.len(), if script_files.len() == 1 { "" } else { "s" } ); // 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::(&script) { // Ok(result) => { // println!("Script executed successfully"); // if !result.is_unit() { // println!("Result: {}", result); // } // } // Err(err) => { // eprintln!("Error executing script: {}", err); // // Exit with error code when a script fails // process::exit(1); // } // } engine.run_with_scope(&mut scope, &script)?; } println!("\nAll scripts executed successfully!"); Ok(()) } /// Recursively collect all .rhai files from a directory /// /// # Arguments /// /// * `dir` - Directory to search /// * `files` - Vector to collect files into /// /// # Returns /// /// Result indicating success or failure fn collect_rhai_files(dir: &Path, files: &mut Vec) -> Result<(), Box> { for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { // Recursively search subdirectories collect_rhai_files(&path, files)?; } else if path.is_file() { // Check if it's a .rhai file if let Some(extension) = path.extension() { if extension == "rhai" { files.push(path); } } } } Ok(()) }