feat: Add herodo package to workspace

- Added the `herodo` package to the workspace.
- Updated the MONOREPO_CONVERSION_PLAN.md to reflect
  the completion of the herodo package conversion.
- Updated README.md and build_herodo.sh to reflect the
  new package structure.
- Created herodo/Cargo.toml, herodo/README.md,
  herodo/src/main.rs, herodo/src/lib.rs, and
  herodo/tests/integration_tests.rs and
  herodo/tests/unit_tests.rs.
This commit is contained in:
Mahmoud-Emad
2025-06-23 13:19:20 +03:00
parent b737cd6337
commit c94467c205
12 changed files with 709 additions and 71 deletions

127
herodo/src/lib.rs Normal file
View File

@@ -0,0 +1,127 @@
//! 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;
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<dyn Error>> {
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();
// 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<PathBuf> = 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::<rhai::Dynamic>(&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);
}
}
}
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<PathBuf>) -> Result<(), Box<dyn Error>> {
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(())
}