...
This commit is contained in:
parent
c8fcdfcbb2
commit
5764950949
@ -4,4 +4,3 @@
|
|||||||
|
|
||||||
// Simple example of exposing Rhai functions to a hashmap
|
// Simple example of exposing Rhai functions to a hashmap
|
||||||
pub mod simple_example;
|
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
|
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
|
// Calculate factorial
|
||||||
fn factorial(n) {
|
fn factorial(n) {
|
||||||
if n <= 1 {
|
// Ensure consistent integer type using to_int()
|
||||||
|
let num = n.to_int();
|
||||||
|
if num <= 1 {
|
||||||
return 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",
|
test_function(&integration, "test_utils:count_words",
|
||||||
vec![Dynamic::from("this is a test sentence")])?;
|
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",
|
test_function(&integration, "test_utils:factorial",
|
||||||
vec![Dynamic::from(5)])?;
|
vec![Dynamic::from(5)])?;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user