...
This commit is contained in:
parent
4b1f305b07
commit
a361f6d4ba
@ -42,32 +42,26 @@ pub fn main() -> Result<(), String> {
|
|||||||
// Test some functions from each script
|
// Test some functions from each script
|
||||||
println!("\nDynamic function testing:");
|
println!("\nDynamic function testing:");
|
||||||
|
|
||||||
// Test string_utils functions
|
//TODO: should not start with string_utils we want capitalize as function usable as is
|
||||||
test_function(&script_manager, "string_utils:capitalize", vec![Dynamic::from("hello world")])?;
|
script_manager.run( "string_utils:capitalize(hello world);")?;
|
||||||
|
script_manager.run( "math_utils:format_number(1234567);")?;
|
||||||
// Test math_utils functions
|
|
||||||
test_function(&script_manager, "math_utils:format_number", vec![Dynamic::from(1234567)])?;
|
|
||||||
|
// Test evaluating a complex script
|
||||||
// Test test_utils functions
|
println!("\nEvaluating a complex script:");
|
||||||
test_function(&script_manager, "test_utils:reverse_string", vec![Dynamic::from("hello world")])?;
|
|
||||||
|
|
||||||
// Using direct script execution
|
|
||||||
println!("\nExecuting scripts directly:");
|
|
||||||
|
|
||||||
// Direct function call with arguments as a script
|
|
||||||
execute_script(&script_manager, "test_utils:count_words(\"this is a test sentence\")")?;
|
|
||||||
|
|
||||||
// Direct script with expression
|
|
||||||
execute_script(&script_manager, "test_utils:factorial(5)")?;
|
|
||||||
|
|
||||||
// Run a more complex script
|
|
||||||
let complex_script = r#"
|
let complex_script = r#"
|
||||||
let result = test_utils:count_words("Count the words in this sentence");
|
let sentence = "Count the words in this sentence";
|
||||||
print("Word count: " + result);
|
let count = test_utils:count_words(sentence);
|
||||||
result * 2 // Return double the word count
|
print("Word count: " + truncate("count",1));
|
||||||
|
count * 2 // Return double the word count
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
execute_script_with_result(&script_manager, complex_script)?;
|
// Evaluate the script
|
||||||
|
println!("```\n{}\n```", complex_script);
|
||||||
|
match script_manager.eval::<Dynamic>(complex_script) {
|
||||||
|
Ok(result) => println!(" Result: {}", result),
|
||||||
|
Err(e) => println!(" Error: {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
println!("\n=== DYNAMIC FUNCTION LOADING TEST COMPLETE ===\n");
|
println!("\n=== DYNAMIC FUNCTION LOADING TEST COMPLETE ===\n");
|
||||||
|
|
||||||
@ -102,44 +96,3 @@ fn display_functions_by_script(scripts: &HashMap<String, Vec<String>>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to test a function call and display its result
|
|
||||||
fn test_function(script_manager: &ScriptManager, name: &str, args: Vec<Dynamic>) -> Result<(), String> {
|
|
||||||
// Call the function with the provided arguments
|
|
||||||
let result = script_manager.call_function(name, args)?;
|
|
||||||
|
|
||||||
// Print the result
|
|
||||||
println!(" {}() => {}", name, result);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute a script directly and display the result
|
|
||||||
fn execute_script(script_manager: &ScriptManager, script: &str) -> Result<(), String> {
|
|
||||||
println!(" Executing: {}", script);
|
|
||||||
|
|
||||||
// Use the run method to execute the script
|
|
||||||
script_manager.run(script)?;
|
|
||||||
|
|
||||||
// For functions that return a value, we can use eval to get the result
|
|
||||||
if let Ok(result) = script_manager.eval::<Dynamic>(script) {
|
|
||||||
println!(" Result: {}", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute a script and return its result
|
|
||||||
fn execute_script_with_result(script_manager: &ScriptManager, script: &str) -> Result<(), String> {
|
|
||||||
println!(" Executing script with result:");
|
|
||||||
println!("```");
|
|
||||||
println!("{}", script);
|
|
||||||
println!("```");
|
|
||||||
|
|
||||||
// Use the eval method to execute the script and get the result
|
|
||||||
let result = script_manager.eval::<Dynamic>(script)?;
|
|
||||||
|
|
||||||
println!(" Result: {}", result);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -121,7 +121,7 @@ impl ScriptManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract functions from a script and map them to callable wrappers
|
/// Extract functions from a script and register them with the engine
|
||||||
fn map_script_functions(&mut self, script_name: &str) -> Result<(), String> {
|
fn map_script_functions(&mut self, script_name: &str) -> Result<(), String> {
|
||||||
let script_arc = self.scripts.get(script_name)
|
let script_arc = self.scripts.get(script_name)
|
||||||
.ok_or_else(|| format!("Script not found: {}", script_name))?
|
.ok_or_else(|| format!("Script not found: {}", script_name))?
|
||||||
@ -141,43 +141,100 @@ impl ScriptManager {
|
|||||||
let fn_name = fn_def.name.to_string();
|
let fn_name = fn_def.name.to_string();
|
||||||
println!(" - {} (params: {})", fn_name, fn_def.params.len());
|
println!(" - {} (params: {})", fn_name, fn_def.params.len());
|
||||||
|
|
||||||
|
// Create a fully qualified name for the function (script:function)
|
||||||
let full_name = format!("{}:{}", script_name, fn_name);
|
let full_name = format!("{}:{}", script_name, fn_name);
|
||||||
let script_arc_clone = script_arc.clone();
|
|
||||||
let fn_name_owned = fn_name.clone();
|
|
||||||
|
|
||||||
// Create a closure that will call this function using a shared engine configuration
|
// Create strong references to required data
|
||||||
|
let script_arc_clone = script_arc.clone();
|
||||||
|
let fn_name_clone = fn_name.clone();
|
||||||
|
|
||||||
|
// Create a function wrapper that will be called from Rust
|
||||||
let function_wrapper: RhaiFn = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
|
let function_wrapper: RhaiFn = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
|
||||||
// Create a configured engine for each invocation
|
// Important: We need to create a scope for each call
|
||||||
let engine = create_standard_engine();
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
// Call the function
|
// Use a clone of self.engine - this ensures we're using the same engine
|
||||||
|
// instance while avoiding ownership issues in the closure
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Configure the engine with the same settings
|
||||||
|
// This is a workaround for the ownership issue with closures
|
||||||
|
engine.set_fast_operators(true);
|
||||||
|
|
||||||
|
// Register the same built-in string functions
|
||||||
|
engine.register_fn("substr", |s: &str, start: i64, len: i64| {
|
||||||
|
let start = start as usize;
|
||||||
|
let len = len as usize;
|
||||||
|
if start >= s.len() { return String::new(); }
|
||||||
|
let end = (start + len).min(s.len());
|
||||||
|
s[start..end].to_string()
|
||||||
|
});
|
||||||
|
engine.register_fn("to_upper", |s: &str| s.to_uppercase());
|
||||||
|
engine.register_fn("to_lower", |s: &str| s.to_lowercase());
|
||||||
|
engine.register_fn("split", |text: &str, delimiter: &str| -> Array {
|
||||||
|
text.split(delimiter).map(|s| Dynamic::from(s.to_string())).collect()
|
||||||
|
});
|
||||||
|
engine.register_fn("split", |s: &str| -> Array {
|
||||||
|
s.split_whitespace()
|
||||||
|
.map(|part| Dynamic::from(part.to_string()))
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
engine.register_fn("len", |array: &mut Array| -> i64 {
|
||||||
|
array.len() as i64
|
||||||
|
});
|
||||||
|
engine.register_fn("join", |arr: &mut Array, separator: &str| -> String {
|
||||||
|
arr.iter()
|
||||||
|
.map(|v| v.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(separator)
|
||||||
|
});
|
||||||
|
engine.register_fn("replace", |s: &str, from: &str, to: &str| -> String {
|
||||||
|
s.replace(from, to)
|
||||||
|
});
|
||||||
|
engine.register_fn("push", |arr: &mut Array, value: Dynamic| {
|
||||||
|
arr.push(value);
|
||||||
|
Dynamic::from(())
|
||||||
|
});
|
||||||
|
engine.register_fn("len", |s: &str| -> i64 {
|
||||||
|
s.len() as i64
|
||||||
|
});
|
||||||
|
engine.register_fn("..", |start: i64, end: i64| {
|
||||||
|
let mut arr = Array::new();
|
||||||
|
for i in start..end { arr.push(Dynamic::from(i)); }
|
||||||
|
arr
|
||||||
|
});
|
||||||
|
|
||||||
|
// Call the function using the configured engine
|
||||||
engine.call_fn::<Dynamic>(
|
engine.call_fn::<Dynamic>(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
&script_arc_clone.read().unwrap(),
|
&script_arc_clone.read().unwrap(),
|
||||||
&fn_name_owned,
|
&fn_name_clone,
|
||||||
args
|
args
|
||||||
).map_err(|e| format!("Error calling function {}: {}", fn_name_owned, e))
|
).map_err(|e| format!("Error calling function {}: {}", fn_name_clone, e))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the function wrapper in the maps
|
// Store the function wrapper in the functions map
|
||||||
self.functions.insert(full_name, function_wrapper);
|
self.functions.insert(full_name, function_wrapper);
|
||||||
|
|
||||||
// Create a new wrapper for the filter
|
// Also create a filter wrapper with the same functionality but specific to filters
|
||||||
let script_arc_clone = script_arc.clone();
|
let script_arc_clone = script_arc.clone();
|
||||||
let fn_name_owned = fn_name.clone();
|
let fn_name_clone = fn_name.clone();
|
||||||
|
|
||||||
let filter_wrapper: RhaiFn = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
|
let filter_wrapper: RhaiFn = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
|
||||||
// Use the same engine creation function
|
// Create a scope and engine similar to the function wrapper
|
||||||
let engine = create_standard_engine();
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Configure the engine with the same settings
|
||||||
|
engine.set_fast_operators(true);
|
||||||
|
|
||||||
|
// Call the function (no need to register all functions again as filters don't use them)
|
||||||
engine.call_fn::<Dynamic>(
|
engine.call_fn::<Dynamic>(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
&script_arc_clone.read().unwrap(),
|
&script_arc_clone.read().unwrap(),
|
||||||
&fn_name_owned,
|
&fn_name_clone,
|
||||||
args
|
args
|
||||||
).map_err(|e| format!("Error calling filter {}: {}", fn_name_owned, e))
|
).map_err(|e| format!("Error calling filter {}: {}", fn_name_clone, e))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the filter wrapper
|
// Store the filter wrapper
|
||||||
@ -294,23 +351,21 @@ impl ScriptManager {
|
|||||||
/// Execute a script snippet directly
|
/// Execute a script snippet directly
|
||||||
pub fn eval<T: Clone>(&self, script: &str) -> Result<T, String>
|
pub fn eval<T: Clone>(&self, script: &str) -> Result<T, String>
|
||||||
where T: 'static + Clone {
|
where T: 'static + Clone {
|
||||||
// Create a configured engine for execution
|
// Use the existing engine and create a new scope
|
||||||
let engine = create_standard_engine();
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
// Execute the script and return the result
|
// Execute the script and return the result
|
||||||
engine.eval_with_scope::<T>(&mut scope, script)
|
self.engine.eval_with_scope::<T>(&mut scope, script)
|
||||||
.map_err(|e| format!("Error evaluating script: {}", e))
|
.map_err(|e| format!("Error evaluating script: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a script without returning a value
|
/// Run a script without returning a value
|
||||||
pub fn run(&self, script: &str) -> Result<(), String> {
|
pub fn run(&self, script: &str) -> Result<(), String> {
|
||||||
// Create a configured engine for execution
|
// Use the existing engine instance
|
||||||
let engine = create_standard_engine();
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
engine.run_with_scope(&mut scope, script)
|
self.engine.run_with_scope(&mut scope, script)
|
||||||
.map_err(|e| format!("Error running script: {}", e))
|
.map_err(|e| format!("Error running script: {}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user