This commit is contained in:
kristof 2025-04-03 10:50:58 +02:00
parent c8fcdfcbb2
commit 5764950949
4 changed files with 4 additions and 135 deletions

View File

@ -4,4 +4,3 @@
// Simple example of exposing Rhai functions to a hashmap
pub mod simple_example;
pub mod rhai_function_map;

View File

@ -1,108 +0,0 @@
//! Rhai Function Map Example
//!
//! This module demonstrates how to expose Rhai scripts dynamically to Rust
//! and make the functions available in a hashmap for a rendering engine.
use rhai::{Engine, AST, Scope, Dynamic, FnAccess};
use std::collections::HashMap;
use std::path::Path;
use std::fs;
use std::sync::Arc;
/// Type alias for a Rhai function that can be called from Rust
pub type RhaiFunction = Box<dyn Fn(Vec<Dynamic>) -> Result<Dynamic, String>>;
/// RhaiFunctionMap loads Rhai scripts and exposes their functions in a hashmap
pub struct RhaiFunctionMap {
engine: Engine,
scripts: HashMap<String, AST>,
functions: HashMap<String, RhaiFunction>,
}
impl RhaiFunctionMap {
/// Create a new RhaiFunctionMap
pub fn new() -> Self {
// Create a standard engine with string operations
let engine = Engine::new();
Self {
engine,
scripts: HashMap::new(),
functions: HashMap::new(),
}
}
/// Load a Rhai script from a file
pub fn load_script(&mut self, name: &str, path: impl AsRef<Path>) -> Result<(), String> {
let path = path.as_ref();
println!("Loading Rhai script: {}", path.display());
// Read the script file
let script_content = fs::read_to_string(path)
.map_err(|e| format!("Failed to read script file {}: {}", path.display(), e))?;
// Compile the script
let ast = self.engine.compile(&script_content)
.map_err(|e| format!("Failed to compile script {}: {}", name, e))?;
// Store the compiled AST
self.scripts.insert(name.to_string(), ast);
// Dynamically discover and register functions from the script
self.register_script_functions(name)?;
Ok(())
}
/// Dynamically register all functions from a script
fn register_script_functions(&mut self, script_name: &str) -> Result<(), String> {
let ast = self.scripts.get(script_name)
.ok_or_else(|| format!("Script not found: {}", script_name))?
.clone();
// Use iter_functions to get all defined functions in the script
let function_defs: Vec<_> = ast.iter_functions().collect();
println!("Found {} functions in script '{}':", function_defs.len(), script_name);
// Register each function we found
for fn_def in function_defs {
let fn_name = fn_def.name.to_string();
println!(" - {} (params: {})", fn_name, fn_def.params.len());
let full_name = format!("{}:{}", script_name, fn_name);
let fn_name_owned = fn_name.clone();
let ast_clone = ast.clone();
// Create a closure that will call this function
let function = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
let engine = Engine::new();
let mut scope = Scope::new();
// Call the function
engine.call_fn::<Dynamic>(&mut scope, &ast_clone, &fn_name_owned, args)
.map_err(|e| format!("Error calling function {}: {}", fn_name_owned, e))
});
// Store the function in the map
self.functions.insert(full_name, function);
}
Ok(())
}
/// Get all functions as a hashmap
pub fn get_functions(&self) -> &HashMap<String, RhaiFunction> {
&self.functions
}
/// Call a function by its full name (script:function)
pub fn call_function(&self, name: &str, args: Vec<Dynamic>) -> Result<Dynamic, String> {
if let Some(function) = self.functions.get(name) {
function(args)
} else {
Err(format!("Function not found: {}", name))
}
}
}

View File

@ -45,31 +45,12 @@ fn count_words(text) {
count
}
// Generate a repeat pattern
// Modified to use a while loop instead of a range (which wasn't supported)
fn repeat(text, times) {
// Simplest possible implementation
// Hardcoded for various times values to avoid any operators
if times == 0 {
return "";
}
if times == 1 {
return text;
}
if times == 2 {
return text + text;
}
// For times == 3 or any other value
return text + text + text;
}
// Calculate factorial
fn factorial(n) {
if n <= 1 {
// Ensure consistent integer type using to_int()
let num = n.to_int();
if num <= 1 {
return 1;
}
n * factorial(n-1)
num * factorial(num-1)
}

View File

@ -48,9 +48,6 @@ pub fn test_dynamic_loading() -> Result<(), String> {
test_function(&integration, "test_utils:count_words",
vec![Dynamic::from("this is a test sentence")])?;
test_function(&integration, "test_utils:repeat",
vec![Dynamic::from("abc"), Dynamic::from(3)])?;
test_function(&integration, "test_utils:factorial",
vec![Dynamic::from(5)])?;