...
This commit is contained in:
parent
c8fcdfcbb2
commit
5764950949
@ -4,4 +4,3 @@
|
||||
|
||||
// Simple example of exposing Rhai functions to a hashmap
|
||||
pub mod simple_example;
|
||||
pub mod rhai_function_map;
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)])?;
|
||||
|
||||
|
Reference in New Issue
Block a user