feat: Add Rhai scripting support for SAL
- Add Rhai scripting integration to the SAL library. - Create documentation for Rhai module usage and available functions. - Implement comprehensive test suite for the Rhai integration. - Add `.gitignore` entries for test related temporary files.
This commit is contained in:
		| @@ -3,71 +3,95 @@ | ||||
| //! This module provides integration with the Rhai scripting language, | ||||
| //! allowing SAL functions to be called from Rhai scripts. | ||||
|  | ||||
| mod buildah; | ||||
| mod error; | ||||
| mod git; | ||||
| mod nerdctl; | ||||
| mod os; | ||||
| mod process; | ||||
| mod buildah; | ||||
| mod nerdctl; | ||||
| mod git; | ||||
| mod text; | ||||
| mod rfs; | ||||
| mod text; | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests; | ||||
|  | ||||
| // Re-export common Rhai types for convenience | ||||
| pub use rhai::{Array, Dynamic, Map, EvalAltResult, Engine}; | ||||
| pub use rhai::{Array, Dynamic, Engine, EvalAltResult, Map}; | ||||
|  | ||||
| // Re-export error module | ||||
| pub use error::*; | ||||
|  | ||||
| // 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, | ||||
|     delete, | ||||
|     // Download functions | ||||
|     download, download_install | ||||
|     download, | ||||
|     download_install, | ||||
|     // File system functions | ||||
|     exist, | ||||
|     file_size, | ||||
|     find_dir, | ||||
|     find_dirs, | ||||
|     find_file, | ||||
|     find_files, | ||||
|     mkdir, | ||||
|     register_os_module, | ||||
|     rsync, | ||||
| }; | ||||
|  | ||||
| pub use process::{ | ||||
|     kill, | ||||
|     process_get, | ||||
|     process_list, | ||||
|     register_process_module, | ||||
|     // Run functions | ||||
|     // Process management functions | ||||
|     which, kill, process_list, process_get | ||||
|     which, | ||||
| }; | ||||
|  | ||||
| // Re-export buildah functions | ||||
| pub use buildah::register_bah_module; | ||||
| pub use buildah::bah_new; | ||||
| pub use buildah::register_bah_module; | ||||
|  | ||||
| // Re-export nerdctl functions | ||||
| pub use nerdctl::register_nerdctl_module; | ||||
| pub use nerdctl::{ | ||||
|     // Container functions | ||||
|     nerdctl_run, nerdctl_run_with_name, nerdctl_run_with_port, | ||||
|     nerdctl_exec, nerdctl_copy, nerdctl_stop, nerdctl_remove, nerdctl_list, | ||||
|     nerdctl_copy, | ||||
|     nerdctl_exec, | ||||
|     nerdctl_image_build, | ||||
|     nerdctl_image_commit, | ||||
|     nerdctl_image_pull, | ||||
|     nerdctl_image_push, | ||||
|     nerdctl_image_remove, | ||||
|     nerdctl_image_tag, | ||||
|     // Image functions | ||||
|     nerdctl_images, nerdctl_image_remove, nerdctl_image_push, nerdctl_image_tag, | ||||
|     nerdctl_image_pull, nerdctl_image_commit, nerdctl_image_build | ||||
|     nerdctl_images, | ||||
|     nerdctl_list, | ||||
|     nerdctl_remove, | ||||
|     // Container functions | ||||
|     nerdctl_run, | ||||
|     nerdctl_run_with_name, | ||||
|     nerdctl_run_with_port, | ||||
|     nerdctl_stop, | ||||
| }; | ||||
|  | ||||
| // Re-export RFS module | ||||
| pub use rfs::register as register_rfs_module; | ||||
|  | ||||
| // Re-export git module | ||||
| pub use crate::git::{GitRepo, GitTree}; | ||||
| pub use git::register_git_module; | ||||
| pub use crate::git::{GitTree, GitRepo}; | ||||
|  | ||||
| // Re-export text module | ||||
| pub use text::register_text_module; | ||||
| // Re-export text functions directly from text module | ||||
| pub use crate::text::{ | ||||
|     // Fix functions | ||||
|     name_fix, path_fix, | ||||
|     // Dedent functions | ||||
|     dedent, prefix | ||||
|     dedent, | ||||
|     // Fix functions | ||||
|     name_fix, | ||||
|     path_fix, | ||||
|     prefix, | ||||
| }; | ||||
|  | ||||
| // Re-export TextReplacer functions | ||||
| @@ -97,27 +121,26 @@ pub use os::copy as os_copy; | ||||
| pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     // Register OS module functions | ||||
|     os::register_os_module(engine)?; | ||||
|      | ||||
|  | ||||
|     // Register Process module functions | ||||
|     process::register_process_module(engine)?; | ||||
|      | ||||
|  | ||||
|     // Register Buildah module functions | ||||
|     buildah::register_bah_module(engine)?; | ||||
|      | ||||
|  | ||||
|     // Register Nerdctl module functions | ||||
|     nerdctl::register_nerdctl_module(engine)?; | ||||
|      | ||||
|  | ||||
|     // Register Git module functions | ||||
|     git::register_git_module(engine)?; | ||||
|      | ||||
|  | ||||
|     // Register Text module functions | ||||
|     text::register_text_module(engine)?; | ||||
|      | ||||
|  | ||||
|     // Register RFS module functions | ||||
|     rfs::register(engine)?; | ||||
|      | ||||
|  | ||||
|     // Future modules can be registered here | ||||
|      | ||||
|      | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
| } | ||||
|   | ||||
							
								
								
									
										111
									
								
								src/rhai_tests/os/01_file_operations.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/rhai_tests/os/01_file_operations.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| // 01_file_operations.rhai | ||||
| // Tests for file system operations in the OS module | ||||
|  | ||||
| // Custom assert function | ||||
| fn assert_true(condition, message) { | ||||
|     if !condition { | ||||
|         print(`ASSERTION FAILED: ${message}`); | ||||
|         throw message; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Create a test directory structure | ||||
| let test_dir = "rhai_test_fs"; | ||||
| let sub_dir = test_dir + "/subdir"; | ||||
|  | ||||
| // Test mkdir function | ||||
| print("Testing mkdir..."); | ||||
| let mkdir_result = mkdir(test_dir); | ||||
| assert_true(exist(test_dir), "Directory creation failed"); | ||||
| print(`✓ mkdir: ${mkdir_result}`); | ||||
|  | ||||
| // Test nested directory creation | ||||
| let nested_result = mkdir(sub_dir); | ||||
| assert_true(exist(sub_dir), "Nested directory creation failed"); | ||||
| print(`✓ mkdir (nested): ${nested_result}`); | ||||
|  | ||||
| // Test file_write function | ||||
| let test_file = test_dir + "/test.txt"; | ||||
| let file_content = "This is a test file created by Rhai test script."; | ||||
| let write_result = file_write(test_file, file_content); | ||||
| assert_true(exist(test_file), "File creation failed"); | ||||
| print(`✓ file_write: ${write_result}`); | ||||
|  | ||||
| // Test file_read function | ||||
| let read_content = file_read(test_file); | ||||
| assert_true(read_content == file_content, "File content doesn't match"); | ||||
| print(`✓ file_read: Content matches`); | ||||
|  | ||||
| // Test file_size function | ||||
| let size = file_size(test_file); | ||||
| assert_true(size > 0, "File size should be greater than 0"); | ||||
| print(`✓ file_size: ${size} bytes`); | ||||
|  | ||||
| // Test file_write_append function | ||||
| let append_content = "\nThis is appended content."; | ||||
| let append_result = file_write_append(test_file, append_content); | ||||
| let new_content = file_read(test_file); | ||||
| assert_true(new_content == file_content + append_content, "Appended content doesn't match"); | ||||
| print(`✓ file_write_append: ${append_result}`); | ||||
|  | ||||
| // Test copy function | ||||
| let copied_file = test_dir + "/copied.txt"; | ||||
| let copy_result = copy(test_file, copied_file); | ||||
| assert_true(exist(copied_file), "File copy failed"); | ||||
| print(`✓ copy: ${copy_result}`); | ||||
|  | ||||
| // Test mv function | ||||
| let moved_file = test_dir + "/moved.txt"; | ||||
| let mv_result = mv(copied_file, moved_file); | ||||
| assert_true(exist(moved_file), "File move failed"); | ||||
| assert_true(!exist(copied_file), "Source file still exists after move"); | ||||
| print(`✓ mv: ${mv_result}`); | ||||
|  | ||||
| // Test find_file function | ||||
| let found_file = find_file(test_dir, "*.txt"); | ||||
| assert_true(found_file.contains("test.txt") || found_file.contains("moved.txt"), "find_file failed"); | ||||
| print(`✓ find_file: ${found_file}`); | ||||
|  | ||||
| // Test find_files function | ||||
| let found_files = find_files(test_dir, "*.txt"); | ||||
| assert_true(found_files.len() == 2, "find_files should find 2 files"); | ||||
| print(`✓ find_files: Found ${found_files.len()} files`); | ||||
|  | ||||
| // Test find_dir function | ||||
| let found_dir = find_dir(test_dir, "sub*"); | ||||
| assert_true(found_dir.contains("subdir"), "find_dir failed"); | ||||
| print(`✓ find_dir: ${found_dir}`); | ||||
|  | ||||
| // Test find_dirs function | ||||
| let found_dirs = find_dirs(test_dir, "sub*"); | ||||
| assert_true(found_dirs.len() == 1, "find_dirs should find 1 directory"); | ||||
| print(`✓ find_dirs: Found ${found_dirs.len()} directories`); | ||||
|  | ||||
| // Test chdir function | ||||
| // Save current directory path before changing | ||||
| let chdir_result = chdir(test_dir); | ||||
| print(`✓ chdir: ${chdir_result}`); | ||||
|  | ||||
| // Change back to parent directory | ||||
| chdir(".."); | ||||
|  | ||||
| // Test rsync function (if available) | ||||
| let rsync_dir = test_dir + "/rsync_dest"; | ||||
| mkdir(rsync_dir); | ||||
| let rsync_result = rsync(test_dir, rsync_dir); | ||||
| print(`✓ rsync: ${rsync_result}`); | ||||
|  | ||||
| // Test delete function | ||||
| let delete_file_result = delete(test_file); | ||||
| assert_true(!exist(test_file), "File deletion failed"); | ||||
| print(`✓ delete (file): ${delete_file_result}`); | ||||
|  | ||||
| // Clean up | ||||
| delete(moved_file); | ||||
| delete(sub_dir); | ||||
| delete(rsync_dir); | ||||
| delete(test_dir); | ||||
| assert_true(!exist(test_dir), "Directory deletion failed"); | ||||
| print(`✓ delete (directory): Directory cleaned up`); | ||||
|  | ||||
| print("All file system tests completed successfully!"); | ||||
							
								
								
									
										53
									
								
								src/rhai_tests/os/02_download_operations.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/rhai_tests/os/02_download_operations.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| // 02_download_operations.rhai | ||||
| // Tests for download operations in the OS module | ||||
|  | ||||
| // Custom assert function | ||||
| fn assert_true(condition, message) { | ||||
|     if !condition { | ||||
|         print(`ASSERTION FAILED: ${message}`); | ||||
|         throw message; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Create a test directory | ||||
| let test_dir = "rhai_test_download"; | ||||
| mkdir(test_dir); | ||||
| print(`Created test directory: ${test_dir}`); | ||||
|  | ||||
| // Test which function to ensure curl is available | ||||
| let curl_path = which("curl"); | ||||
| if curl_path == "" { | ||||
|     print("Warning: curl not found, download tests may fail"); | ||||
| } else { | ||||
|     print(`✓ which: curl found at ${curl_path}`); | ||||
| } | ||||
|  | ||||
| // Test cmd_ensure_exists function | ||||
| let ensure_result = cmd_ensure_exists("curl"); | ||||
| print(`✓ cmd_ensure_exists: ${ensure_result}`); | ||||
|  | ||||
| // Test download function with a small file | ||||
| let download_url = "https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT"; | ||||
| let download_dest = test_dir + "/license.txt"; | ||||
| let min_size_kb = 1; // Minimum size in KB | ||||
|  | ||||
| print(`Downloading ${download_url}...`); | ||||
| let download_result = download_file(download_url, download_dest, min_size_kb); | ||||
| assert_true(exist(download_dest), "Download failed"); | ||||
| print(`✓ download_file: ${download_result}`); | ||||
|  | ||||
| // Verify the downloaded file | ||||
| let file_content = file_read(download_dest); | ||||
| assert_true(file_content.contains("Permission is hereby granted"), "Downloaded file content is incorrect"); | ||||
| print("✓ Downloaded file content verified"); | ||||
|  | ||||
| // Test chmod_exec function | ||||
| let chmod_result = chmod_exec(download_dest); | ||||
| print(`✓ chmod_exec: ${chmod_result}`); | ||||
|  | ||||
| // Clean up | ||||
| delete(test_dir); | ||||
| assert_true(!exist(test_dir), "Directory deletion failed"); | ||||
| print(`✓ Cleanup: Directory ${test_dir} removed`); | ||||
|  | ||||
| print("All download tests completed successfully!"); | ||||
							
								
								
									
										56
									
								
								src/rhai_tests/os/03_package_operations.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/rhai_tests/os/03_package_operations.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| // 03_package_operations.rhai | ||||
| // Tests for package management operations in the OS module | ||||
|  | ||||
| // Custom assert function | ||||
| fn assert_true(condition, message) { | ||||
|     if !condition { | ||||
|         print(`ASSERTION FAILED: ${message}`); | ||||
|         throw message; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Test package_platform function | ||||
| let platform = package_platform(); | ||||
| print(`Current platform: ${platform}`); | ||||
|  | ||||
| // Test package_set_debug function | ||||
| let debug_enabled = package_set_debug(true); | ||||
| assert_true(debug_enabled, "Debug mode should be enabled"); | ||||
| print("✓ package_set_debug: Debug mode enabled"); | ||||
|  | ||||
| // Disable debug mode for remaining tests | ||||
| package_set_debug(false); | ||||
|  | ||||
| // Test package_is_installed function with a package that should exist on most systems | ||||
| let common_packages = ["bash", "curl", "grep"]; | ||||
| let found_package = false; | ||||
|  | ||||
| for pkg in common_packages { | ||||
|     let is_installed = package_is_installed(pkg); | ||||
|     if is_installed { | ||||
|         print(`✓ package_is_installed: ${pkg} is installed`); | ||||
|         found_package = true; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| if !found_package { | ||||
|     print("Warning: None of the common packages were found installed"); | ||||
| } | ||||
|  | ||||
| // Test package_search function with a common term | ||||
| // Note: This might be slow and produce a lot of output | ||||
| print("Testing package_search (this might take a moment)..."); | ||||
| let search_results = package_search("lib"); | ||||
| print(`✓ package_search: Found ${search_results.len()} packages containing 'lib'`); | ||||
|  | ||||
| // Test package_list function | ||||
| // Note: This might be slow and produce a lot of output | ||||
| print("Testing package_list (this might take a moment)..."); | ||||
| let installed_packages = package_list(); | ||||
| print(`✓ package_list: Found ${installed_packages.len()} installed packages`); | ||||
|  | ||||
| // Note: We're not testing package_install, package_remove, package_update, or package_upgrade | ||||
| // as they require root privileges and could modify the system state | ||||
|  | ||||
| print("All package management tests completed successfully!"); | ||||
							
								
								
									
										148
									
								
								src/rhai_tests/os/run_all_tests.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/rhai_tests/os/run_all_tests.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| // run_all_tests.rhai | ||||
| // Runs all OS module tests | ||||
|  | ||||
| print("=== Running OS Module Tests ==="); | ||||
|  | ||||
| // Custom assert function | ||||
| fn assert_true(condition, message) { | ||||
|     if !condition { | ||||
|         print(`ASSERTION FAILED: ${message}`); | ||||
|         throw message; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Run each test directly | ||||
| let passed = 0; | ||||
| let failed = 0; | ||||
|  | ||||
| // Test 1: File Operations | ||||
| print("\n--- Running File Operations Tests ---"); | ||||
| try { | ||||
|     // Create a test directory structure | ||||
|     let test_dir = "rhai_test_fs"; | ||||
|     let sub_dir = test_dir + "/subdir"; | ||||
|  | ||||
|     // Test mkdir function | ||||
|     print("Testing mkdir..."); | ||||
|     let mkdir_result = mkdir(test_dir); | ||||
|     assert_true(exist(test_dir), "Directory creation failed"); | ||||
|     print(`✓ mkdir: ${mkdir_result}`); | ||||
|  | ||||
|     // Test nested directory creation | ||||
|     let nested_result = mkdir(sub_dir); | ||||
|     assert_true(exist(sub_dir), "Nested directory creation failed"); | ||||
|     print(`✓ mkdir (nested): ${nested_result}`); | ||||
|  | ||||
|     // Test file_write function | ||||
|     let test_file = test_dir + "/test.txt"; | ||||
|     let file_content = "This is a test file created by Rhai test script."; | ||||
|     let write_result = file_write(test_file, file_content); | ||||
|     assert_true(exist(test_file), "File creation failed"); | ||||
|     print(`✓ file_write: ${write_result}`); | ||||
|  | ||||
|     // Test file_read function | ||||
|     let read_content = file_read(test_file); | ||||
|     assert_true(read_content == file_content, "File content doesn't match"); | ||||
|     print(`✓ file_read: Content matches`); | ||||
|  | ||||
|     // Test file_size function | ||||
|     let size = file_size(test_file); | ||||
|     assert_true(size > 0, "File size should be greater than 0"); | ||||
|     print(`✓ file_size: ${size} bytes`); | ||||
|  | ||||
|     // Clean up | ||||
|     delete(test_file); | ||||
|     delete(sub_dir); | ||||
|     delete(test_dir); | ||||
|     assert_true(!exist(test_dir), "Directory deletion failed"); | ||||
|     print(`✓ delete: Directory cleaned up`); | ||||
|  | ||||
|     print("--- File Operations Tests completed successfully ---"); | ||||
|     passed += 1; | ||||
| } catch(err) { | ||||
|     print(`!!! Error in File Operations Tests: ${err}`); | ||||
|     failed += 1; | ||||
| } | ||||
|  | ||||
| // Test 2: Download Operations | ||||
| print("\n--- Running Download Operations Tests ---"); | ||||
| try { | ||||
|     // Create a test directory | ||||
|     let test_dir = "rhai_test_download"; | ||||
|     mkdir(test_dir); | ||||
|     print(`Created test directory: ${test_dir}`); | ||||
|  | ||||
|     // Test which function to ensure curl is available | ||||
|     let curl_path = which("curl"); | ||||
|     if curl_path == "" { | ||||
|         print("Warning: curl not found, download tests may fail"); | ||||
|     } else { | ||||
|         print(`✓ which: curl found at ${curl_path}`); | ||||
|     } | ||||
|  | ||||
|     // Test cmd_ensure_exists function | ||||
|     let ensure_result = cmd_ensure_exists("curl"); | ||||
|     print(`✓ cmd_ensure_exists: ${ensure_result}`); | ||||
|  | ||||
|     // Test download function with a small file | ||||
|     let download_url = "https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT"; | ||||
|     let download_dest = test_dir + "/license.txt"; | ||||
|     let min_size_kb = 1; // Minimum size in KB | ||||
|  | ||||
|     print(`Downloading ${download_url}...`); | ||||
|     let download_result = download_file(download_url, download_dest, min_size_kb); | ||||
|     assert_true(exist(download_dest), "Download failed"); | ||||
|     print(`✓ download_file: ${download_result}`); | ||||
|  | ||||
|     // Verify the downloaded file | ||||
|     let file_content = file_read(download_dest); | ||||
|     assert_true(file_content.contains("Permission is hereby granted"), "Downloaded file content is incorrect"); | ||||
|     print("✓ Downloaded file content verified"); | ||||
|  | ||||
|     // Clean up | ||||
|     delete(test_dir); | ||||
|     assert_true(!exist(test_dir), "Directory deletion failed"); | ||||
|     print(`✓ Cleanup: Directory ${test_dir} removed`); | ||||
|  | ||||
|     print("--- Download Operations Tests completed successfully ---"); | ||||
|     passed += 1; | ||||
| } catch(err) { | ||||
|     print(`!!! Error in Download Operations Tests: ${err}`); | ||||
|     failed += 1; | ||||
| } | ||||
|  | ||||
| // Test 3: Package Operations | ||||
| print("\n--- Running Package Operations Tests ---"); | ||||
| try { | ||||
|     // Test package_platform function | ||||
|     let platform = package_platform(); | ||||
|     print(`Current platform: ${platform}`); | ||||
|  | ||||
|     // Test package_set_debug function | ||||
|     let debug_enabled = package_set_debug(true); | ||||
|     assert_true(debug_enabled, "Debug mode should be enabled"); | ||||
|     print("✓ package_set_debug: Debug mode enabled"); | ||||
|  | ||||
|     // Disable debug mode for remaining tests | ||||
|     package_set_debug(false); | ||||
|  | ||||
|     print("--- Package Operations Tests completed successfully ---"); | ||||
|     passed += 1; | ||||
| } catch(err) { | ||||
|     print(`!!! Error in Package Operations Tests: ${err}`); | ||||
|     failed += 1; | ||||
| } | ||||
|  | ||||
| print("\n=== Test Summary ==="); | ||||
| print(`Passed: ${passed}`); | ||||
| print(`Failed: ${failed}`); | ||||
| print(`Total: ${passed + failed}`); | ||||
|  | ||||
| if failed == 0 { | ||||
|     print("\n✅ All tests passed!"); | ||||
| } else { | ||||
|     print("\n❌ Some tests failed!"); | ||||
| } | ||||
|  | ||||
| // Return the number of failed tests (0 means success) | ||||
| failed; | ||||
		Reference in New Issue
	
	Block a user