This commit represents a major refactoring of our Rhai scripting system, transforming it from a factory-based approach to a more robust system-based architecture with improved hot reloading capabilities. Key Changes: - Renamed package from rhai_factory to rhai_system to better reflect its purpose - Renamed system_factory.rs to factory.rs for consistency and clarity - Implemented support for multiple script files in hot reloading - Added cross-script function calls, allowing functions in one script to call functions in another - Improved file watching to monitor all script files for changes - Enhanced error handling for script compilation failures - Simplified the API with a cleaner create_hot_reloadable_system function - Removed unused modules (error.rs, factory.rs, hot_reload_old.rs, module_cache.rs, relative_resolver.rs) - Updated all tests to work with the new architecture The new architecture: - Uses a System struct that holds references to script paths and provides a clean API - Compiles and merges multiple Rhai script files into a single AST - Automatically detects changes to any script file and recompiles them - Maintains thread safety with proper synchronization primitives - Provides better error messages when scripts fail to compile This refactoring aligns with our BasePathModuleResolver approach for module imports, making the resolution process more predictable and consistent. The hot reload example has been updated to demonstrate the new capabilities, showing how to: 1. Load and execute multiple script files 2. Watch for changes to these files 3. Automatically reload scripts when they change 4. Call functions across different script files All tests are passing, and the example demonstrates the improved functionality.
153 lines
6.3 KiB
Rust
153 lines
6.3 KiB
Rust
use std::thread;
|
|
use std::time::Duration;
|
|
use std::path::PathBuf;
|
|
use std::fs::{self, File};
|
|
use std::io::Write;
|
|
|
|
// Import the create_hot_reloadable_system from the library
|
|
use rhai_system::create_hot_reloadable_system;
|
|
|
|
/// Function to modify a script file with content from another file
|
|
fn modify_script(target_path: &PathBuf, source_path: &str, delay_secs: u64, message: &str) {
|
|
println!("\n🔄 {}", message);
|
|
|
|
// Read the source script content
|
|
let source_script_path = PathBuf::from(source_path);
|
|
let source_content = fs::read_to_string(&source_script_path)
|
|
.expect(&format!("Failed to read source script file: {}", source_path));
|
|
|
|
// Write the content to the target file
|
|
let mut file = File::create(target_path)
|
|
.expect("Failed to open target script file for writing");
|
|
file.write_all(source_content.as_bytes())
|
|
.expect("Failed to write to target script file");
|
|
|
|
println!("✅ Script modified successfully!");
|
|
|
|
// Wait before the next modification if delay is specified
|
|
if delay_secs > 0 {
|
|
thread::sleep(Duration::from_secs(delay_secs));
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Set up the script paths
|
|
let main_script_path = PathBuf::from("examples/hot_reload/script.rhai");
|
|
let utils_script_path = PathBuf::from("examples/hot_reload/utils.rhai");
|
|
println!("Main script path: {:?}", main_script_path);
|
|
println!("Utils script path: {:?}", utils_script_path);
|
|
|
|
// Initialize script.rhai with the content from initial_script.rhai
|
|
let initial_script_path = PathBuf::from("examples/hot_reload/initial_script.rhai");
|
|
let initial_content = fs::read_to_string(&initial_script_path)
|
|
.expect("Failed to read initial script file");
|
|
|
|
let mut file = File::create(&main_script_path)
|
|
.expect("Failed to open script file for writing");
|
|
file.write_all(initial_content.as_bytes())
|
|
.expect("Failed to write to script file");
|
|
|
|
// Initialize utils.rhai with the content from initial_utils.rhai
|
|
let initial_utils_path = PathBuf::from("examples/hot_reload/initial_utils.rhai");
|
|
let initial_utils_content = fs::read_to_string(&initial_utils_path)
|
|
.expect("Failed to read initial utils file");
|
|
|
|
let mut utils_file = File::create(&utils_script_path)
|
|
.expect("Failed to open utils file for writing");
|
|
utils_file.write_all(initial_utils_content.as_bytes())
|
|
.expect("Failed to write to utils file");
|
|
|
|
// Create the hot-reloadable system with both script paths
|
|
// We're passing a slice with both paths and using None for main_script_index
|
|
// to use the default (first script in the slice)
|
|
let system = create_hot_reloadable_system(&[main_script_path.clone(), utils_script_path.clone()], None)?;
|
|
|
|
// Start a thread that periodically executes the script
|
|
let execution_thread = thread::spawn(move || {
|
|
// Every second, call the greet function from the script
|
|
loop {
|
|
// Call the greet function
|
|
match system.call_fn::<String>("greet", ("User",)) {
|
|
Ok(result) => println!("Execution result: {}", result),
|
|
Err(err) => println!("Error executing script: {}", err),
|
|
}
|
|
|
|
// Call the add function
|
|
match system.call_fn::<i32>("add", (40, 2)) {
|
|
Ok(result) => println!("Add result: {}", result),
|
|
Err(err) => println!("Error executing add function: {}", err),
|
|
}
|
|
|
|
// Call the advanced_calculation function that uses utils.rhai
|
|
match system.call_fn::<i64>("advanced_calculation", (5_i64, 7_i64)) {
|
|
Ok(result) => println!("Advanced calculation result: {}", result),
|
|
Err(err) => println!("Error executing advanced_calculation function: {}", err),
|
|
}
|
|
|
|
// Try to call the multiply function, catch any errors
|
|
match system.call_fn::<i32>("multiply", (40, 2)) {
|
|
Ok(result) => println!("Multiply result: {}", result),
|
|
Err(err) => {
|
|
if err.to_string().contains("function not found") {
|
|
println!("Multiply function not available yet");
|
|
} else {
|
|
println!("Error executing multiply function: {}", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to call the divide function, catch any errors
|
|
match system.call_fn::<i32>("divide", (40, 2)) {
|
|
Ok(result) => println!("Divide result: {}", result),
|
|
Err(err) => {
|
|
if err.to_string().contains("function not found") {
|
|
println!("Divide function not available yet");
|
|
} else {
|
|
println!("Error executing divide function: {}", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wait before the next execution
|
|
thread::sleep(Duration::from_secs(1));
|
|
}
|
|
});
|
|
|
|
// Start a thread to modify the script files at intervals
|
|
let main_script_path_clone = main_script_path.clone();
|
|
let utils_script_path_clone = utils_script_path.clone();
|
|
thread::spawn(move || {
|
|
// Wait 5 seconds before first modification
|
|
thread::sleep(Duration::from_secs(5));
|
|
|
|
// First modification - add multiply function
|
|
modify_script(
|
|
&main_script_path_clone,
|
|
"examples/hot_reload/modified_script.rhai",
|
|
10,
|
|
"Modifying the script to add multiply function..."
|
|
);
|
|
|
|
// Second modification - add divide function
|
|
modify_script(
|
|
&main_script_path_clone,
|
|
"examples/hot_reload/second_modified_script.rhai",
|
|
0,
|
|
"Modifying the script again to add divide function..."
|
|
);
|
|
|
|
// Third modification - modify utils.rhai
|
|
modify_script(
|
|
&utils_script_path_clone,
|
|
"examples/hot_reload/modified_utils.rhai",
|
|
0,
|
|
"Modifying the utils script..."
|
|
);
|
|
});
|
|
|
|
// Wait for the execution thread to finish (it won't, but this keeps the main thread alive)
|
|
execution_thread.join().unwrap();
|
|
|
|
Ok(())
|
|
}
|