refactor: Overhaul Rhai scripting with multi-file hot reloading

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.
This commit is contained in:
Timur Gordon
2025-05-02 21:04:33 +02:00
parent 939b6b4e57
commit 372b7a2772
54 changed files with 5692 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
// Calendar model functions
// Import common utility functions
import "components/common/utils/date_utils" as date_utils;
import "components/common/utils/string_utils" as string_utils;
// Creates a new calendar event
fn create_event(title, description, year, month, day) {
let formatted_title = string_utils::to_upper(title);
let formatted_date = date_utils::format_date(year, month, day);
return #{
title: formatted_title,
description: description,
date: formatted_date,
is_valid: is_valid_event_date(year, month, day)
};
}
// Checks if an event date is valid
fn is_valid_event_date(year, month, day) {
// Convert all parameters to integers to ensure consistent comparison
let y = year.to_int();
let m = month.to_int();
let d = day.to_int();
if m < 1 || m > 12 {
return false;
}
let max_days = date_utils::days_in_month(y, m);
return d >= 1 && d <= max_days;
}

View File

@@ -0,0 +1,31 @@
// Simple date utility functions
// Format a date as YYYY-MM-DD
fn format_date(year, month, day) {
let month_str = month;
let day_str = day;
return year + "-" + month_str + "-" + day_str;
}
// Check if a year is a leap year
fn is_leap_year(year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// Get the number of days in a month
fn days_in_month(year, month) {
if month == 2 {
if is_leap_year(year) {
return 29;
} else {
return 28;
}
}
if month == 4 || month == 6 || month == 9 || month == 11 {
return 30;
}
return 31;
}

View File

@@ -0,0 +1,16 @@
// Simple string utility functions
// Make a string uppercase
fn to_upper(s) {
return s.to_upper();
}
// Make a string lowercase
fn to_lower(s) {
return s.to_lower();
}
// Get string length
fn length(s) {
return s.len();
}

View File

@@ -0,0 +1,28 @@
// Website model functions
// Import common utility functions
import "components/common/utils/string_utils" as string_utils;
// Creates a new website page
fn create_page(title, content, slug) {
let formatted_title = string_utils::to_upper(title);
let slug_length = string_utils::length(slug);
return #{
title: formatted_title,
content: content,
slug: slug,
slug_length: slug_length,
is_valid: is_valid_slug(slug)
};
}
// Validates a page slug
fn is_valid_slug(slug) {
// Simple validation - check if it's not empty and has reasonable length
if slug.len() == 0 || slug.len() > 100 {
return false;
}
return true;
}