...
This commit is contained in:
parent
a361f6d4ba
commit
3b4db8b66b
@ -121,7 +121,7 @@ impl ScriptManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract functions from a script and register them with the engine
|
/// Extract functions from a script and register them directly 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))?
|
||||||
@ -136,7 +136,7 @@ impl ScriptManager {
|
|||||||
// Log found functions for debugging
|
// Log found functions for debugging
|
||||||
println!("Registering functions for script '{}':", script_name);
|
println!("Registering functions for script '{}':", script_name);
|
||||||
|
|
||||||
// Register each function we found
|
// Register each function we found directly into the engine
|
||||||
for fn_def in function_defs {
|
for fn_def in function_defs {
|
||||||
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());
|
||||||
@ -144,106 +144,171 @@ impl ScriptManager {
|
|||||||
// Create a fully qualified name for the function (script:function)
|
// 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);
|
||||||
|
|
||||||
// Create strong references to required data
|
// Create references to required data
|
||||||
let script_arc_clone = script_arc.clone();
|
let script_arc_clone = script_arc.clone();
|
||||||
let fn_name_clone = fn_name.clone();
|
let fn_name_clone = fn_name.clone();
|
||||||
|
|
||||||
// Create a function wrapper that will be called from Rust
|
// Create a wrapper function based on parameter count
|
||||||
|
match fn_def.params.len() {
|
||||||
|
0 => {
|
||||||
|
// Create a function wrapper with no parameters
|
||||||
|
self.register_script_fn0(&full_name, &script_arc_clone, &fn_name_clone);
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
// Create a function wrapper with 1 parameter
|
||||||
|
self.register_script_fn1(&full_name, &script_arc_clone, &fn_name_clone);
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
// Create a function wrapper with 2 parameters
|
||||||
|
self.register_script_fn2(&full_name, &script_arc_clone, &fn_name_clone);
|
||||||
|
},
|
||||||
|
3 => {
|
||||||
|
// Create a function wrapper with 3 parameters
|
||||||
|
self.register_script_fn3(&full_name, &script_arc_clone, &fn_name_clone);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// For functions with more parameters, create a generic wrapper
|
||||||
|
self.register_script_fn_dynamic(&full_name, &script_arc_clone, &fn_name_clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the function wrapper for our maps
|
||||||
|
let script_arc_for_map = script_arc.clone();
|
||||||
|
let fn_name_for_map = fn_name.clone();
|
||||||
|
|
||||||
|
// Create a function wrapper for direct calling
|
||||||
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> {
|
||||||
// Important: We need to create a scope for each call
|
// Create a new scope for this invocation
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
// Use a clone of self.engine - this ensures we're using the same engine
|
// We need a new engine here because we're inside a closure
|
||||||
// instance while avoiding ownership issues in the closure
|
|
||||||
let mut engine = Engine::new();
|
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);
|
engine.set_fast_operators(true);
|
||||||
|
|
||||||
// Register the same built-in string functions
|
// Call the function using the engine
|
||||||
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_for_map.read().unwrap(),
|
||||||
&fn_name_clone,
|
&fn_name_for_map,
|
||||||
args
|
args
|
||||||
).map_err(|e| format!("Error calling function {}: {}", fn_name_clone, e))
|
).map_err(|e| format!("Error calling function {}: {}", fn_name_for_map, e))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the function wrapper in the functions map
|
// Store in our maps for backwards compatibility
|
||||||
self.functions.insert(full_name, function_wrapper);
|
self.functions.insert(full_name, function_wrapper.clone());
|
||||||
|
self.filters.insert(fn_name, function_wrapper);
|
||||||
// Also create a filter wrapper with the same functionality but specific to filters
|
|
||||||
let script_arc_clone = script_arc.clone();
|
|
||||||
let fn_name_clone = fn_name.clone();
|
|
||||||
|
|
||||||
let filter_wrapper: RhaiFn = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
|
|
||||||
// Create a scope and engine similar to the function wrapper
|
|
||||||
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>(
|
|
||||||
&mut scope,
|
|
||||||
&script_arc_clone.read().unwrap(),
|
|
||||||
&fn_name_clone,
|
|
||||||
args
|
|
||||||
).map_err(|e| format!("Error calling filter {}: {}", fn_name_clone, e))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store the filter wrapper
|
|
||||||
self.filters.insert(fn_name, filter_wrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper method to register a script function with 0 parameters
|
||||||
|
fn register_script_fn0(&mut self, full_name: &str, script_arc: &Arc<RwLock<AST>>, fn_name: &str) {
|
||||||
|
let script_arc = script_arc.clone();
|
||||||
|
let fn_name = fn_name.to_string();
|
||||||
|
|
||||||
|
self.engine.register_fn(full_name, move || -> Dynamic {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
let engine = Engine::new(); // We need a temporary engine for the call
|
||||||
|
|
||||||
|
engine.call_fn::<Dynamic>(
|
||||||
|
&mut scope,
|
||||||
|
&script_arc.read().unwrap(),
|
||||||
|
&fn_name,
|
||||||
|
[]
|
||||||
|
).unwrap_or_else(|e| {
|
||||||
|
// Return error as a string if the function call fails
|
||||||
|
Dynamic::from(format!("Error: {}", e))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to register a script function with 1 parameter
|
||||||
|
fn register_script_fn1(&mut self, full_name: &str, script_arc: &Arc<RwLock<AST>>, fn_name: &str) {
|
||||||
|
let script_arc = script_arc.clone();
|
||||||
|
let fn_name = fn_name.to_string();
|
||||||
|
|
||||||
|
self.engine.register_fn(full_name, move |arg1: Dynamic| -> Dynamic {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
engine.call_fn::<Dynamic>(
|
||||||
|
&mut scope,
|
||||||
|
&script_arc.read().unwrap(),
|
||||||
|
&fn_name,
|
||||||
|
[arg1]
|
||||||
|
).unwrap_or_else(|e| {
|
||||||
|
Dynamic::from(format!("Error: {}", e))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to register a script function with 2 parameters
|
||||||
|
fn register_script_fn2(&mut self, full_name: &str, script_arc: &Arc<RwLock<AST>>, fn_name: &str) {
|
||||||
|
let script_arc = script_arc.clone();
|
||||||
|
let fn_name = fn_name.to_string();
|
||||||
|
|
||||||
|
self.engine.register_fn(full_name, move |arg1: Dynamic, arg2: Dynamic| -> Dynamic {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
engine.call_fn::<Dynamic>(
|
||||||
|
&mut scope,
|
||||||
|
&script_arc.read().unwrap(),
|
||||||
|
&fn_name,
|
||||||
|
[arg1, arg2]
|
||||||
|
).unwrap_or_else(|e| {
|
||||||
|
Dynamic::from(format!("Error: {}", e))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to register a script function with 3 parameters
|
||||||
|
fn register_script_fn3(&mut self, full_name: &str, script_arc: &Arc<RwLock<AST>>, fn_name: &str) {
|
||||||
|
let script_arc = script_arc.clone();
|
||||||
|
let fn_name = fn_name.to_string();
|
||||||
|
|
||||||
|
self.engine.register_fn(full_name, move |arg1: Dynamic, arg2: Dynamic, arg3: Dynamic| -> Dynamic {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
engine.call_fn::<Dynamic>(
|
||||||
|
&mut scope,
|
||||||
|
&script_arc.read().unwrap(),
|
||||||
|
&fn_name,
|
||||||
|
[arg1, arg2, arg3]
|
||||||
|
).unwrap_or_else(|e| {
|
||||||
|
Dynamic::from(format!("Error: {}", e))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to register a script function with dynamic parameters
|
||||||
|
fn register_script_fn_dynamic(&mut self, full_name: &str, script_arc: &Arc<RwLock<AST>>, fn_name: &str) {
|
||||||
|
let script_arc = script_arc.clone();
|
||||||
|
let fn_name = fn_name.to_string();
|
||||||
|
|
||||||
|
// For functions with many parameters, just use a HashMp approach instead
|
||||||
|
// Create a function wrapper that can be stored in our maps
|
||||||
|
let script_arc_for_map = script_arc.clone();
|
||||||
|
let fn_name_for_map = fn_name.to_string();
|
||||||
|
|
||||||
|
// Create a function wrapper for direct calling
|
||||||
|
let function_wrapper: RhaiFn = Box::new(move |args: Vec<Dynamic>| -> Result<Dynamic, String> {
|
||||||
|
// Create a new scope for this invocation
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Call the function using the engine
|
||||||
|
engine.call_fn::<Dynamic>(
|
||||||
|
&mut scope,
|
||||||
|
&script_arc_for_map.read().unwrap(),
|
||||||
|
&fn_name_for_map,
|
||||||
|
args
|
||||||
|
).map_err(|e| format!("Error calling function {}: {}", fn_name_for_map, e))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a function by its full name (script:function)
|
/// Get a function by its full name (script:function)
|
||||||
pub fn get_function(&self, name: &str) -> Option<&RhaiFn> {
|
pub fn get_function(&self, name: &str) -> Option<&RhaiFn> {
|
||||||
self.functions.get(name)
|
self.functions.get(name)
|
||||||
|
Reference in New Issue
Block a user