create macros for generating rhai wrappers and add tests

This commit is contained in:
timurgordon
2025-05-12 02:31:45 +03:00
parent 22032f329a
commit 16ad4f5743
11 changed files with 2735 additions and 0 deletions

210
rhai_wrapper/src/lib.rs Normal file
View File

@@ -0,0 +1,210 @@
//! # Rhai Wrapper
//! Provides a macro to simplify wrapping Rust functions for the Rhai scripting engine.
//!
//! This crate provides a macro and utilities to wrap generic Rust functions so they can be registered with the Rhai scripting engine.
//! It currently supports functions with primitive arguments (i64, f64, String) and return values.
use rhai::Map; // Kept Map for traits. Dynamic, INT, FLOAT removed as they are qualified in macro.
/// Trait for converting a Rust struct into a Rhai `Map`.
/// This trait is intended to be derived using `#[derive(ToRhaiMap)]` from the `rhai_macros_derive` crate.
pub trait ToRhaiMap {
/// Converts `&self` into a `rhai::Map`.
fn to_rhai_map(&self) -> Map;
}
/// Trait for converting a Rhai `Map` into a Rust struct.
/// This trait is intended to be derived using `#[derive(FromRhaiMap)]` from the `rhai_macros_derive` crate.
pub trait FromRhaiMap: Sized {
/// Attempts to convert a `rhai::Map` into an instance of `Self`.
/// Returns a `Result` which is `Ok(Self)` on success, or an `Err(String)` describing the error.
fn from_rhai_map(map: Map) -> Result<Self, String>;
}
/// Macro to wrap a Rust function for Rhai
///
/// Usage:
/// ```
/// use rhai_wrapper::wrap_for_rhai;
/// use rhai::{Engine, INT};
/// fn add(a: INT, b: INT) -> INT { a + b }
/// let mut engine = Engine::new();
/// engine.register_fn("add", wrap_for_rhai!(add));
/// let result = engine.eval::<INT>("add(2, 3)").unwrap();
/// assert_eq!(result, 5);
/// ```
#[macro_export]
macro_rules! wrap_for_rhai {
// New arm for functions like: fn name(Vec<rhai::INT>) -> rhai::INT
// Usage: wrap_for_rhai!(my_function_name, Vec<INT> -> INT)
($func:ident, Vec<INT> -> INT) => {
|arr: rhai::Array| -> rhai::INT {
let vec_arg: std::vec::Vec<INT> = arr.into_iter()
.map(|x: rhai::Dynamic| x.as_int().expect("Rhai array element is not an INT or could not be converted"))
.collect();
$func(vec_arg)
}
};
// New arm for functions like: fn name(Vec<String>) -> String
// Usage: wrap_for_rhai!(my_function_name, Vec<String> -> String)
($func:ident, Vec<String> -> String) => {
|arr: rhai::Array| -> String {
let vec_arg: std::vec::Vec<String> = arr.into_iter()
.map(|x: rhai::Dynamic| x.into_string().expect("Rhai array element is not a String or could not be converted"))
.collect();
$func(vec_arg)
}
};
// New arm for functions like: fn name(Vec<FLOAT>) -> FLOAT
// Usage: wrap_for_rhai!(my_function_name, Vec<FLOAT> -> FLOAT)
($func:ident, Vec<FLOAT> -> FLOAT) => {
|arr: rhai::Array| -> rhai::FLOAT {
let vec_arg: std::vec::Vec<FLOAT> = arr.into_iter()
.map(|x: rhai::Dynamic| x.as_float().expect("Rhai array element is not a FLOAT or could not be converted"))
.collect();
$func(vec_arg)
}
};
// Specific arms for Vec<CustomStruct> -> Vec<PrimitiveType>
// These must come BEFORE the Vec<CustomIn> -> Vec<CustomOut> arm.
($func:ident, Vec<$InputStructType:ident> -> Vec<INT>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<rhai::INT> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<FLOAT>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<rhai::FLOAT> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<String>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<String> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<bool>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<bool> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
// Arm for Vec<CustomStructIn> -> Vec<CustomStructOut>
// This must come AFTER specific primitive Vec outputs to allow them to match first.
// Requires $InputStructType::from_rhai_map and $OutputStructType::to_rhai_map
($func:ident, Vec<$InputStructType:ident> -> Vec<$OutputStructType:ident>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect("Rhai array element is not an object map for $InputStructType conversion");
$InputStructType::from_rhai_map(map)
.expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<$OutputStructType> = $func(vec_arg);
result_vec.into_iter()
.map(|item: $OutputStructType| item.to_rhai_map().into()) // Convert struct to rhai::Map, then to rhai::Dynamic
.collect::<rhai::Array>()
}
};
// Func(CustomStruct) -> String
($func:ident, $InputStructType:ident -> String) => {
|arg_dyn: rhai::Dynamic| -> String {
let arg_map = arg_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument not an object map for {}", stringify!($InputStructType)));
let arg_struct = $InputStructType::from_rhai_map(arg_map)
.expect(&format!("Failed to convert map to {}", stringify!($InputStructType)));
$func(arg_struct)
}
};
// Func(String, INT, INT) -> CustomReturnType
($func:ident, String, INT, INT -> $ReturnType:ident) => {
|id_str: rhai::ImmutableString, cx_int: rhai::INT, cy_int: rhai::INT| -> rhai::Dynamic {
let id = id_str.to_string();
// cx_int and cy_int are already rhai::INT, matching typical Rust INT type alias for i64
let result_struct: $ReturnType = $func(id, cx_int, cy_int);
let result_map = result_struct.to_rhai_map();
rhai::Dynamic::from(result_map)
}
};
// Func(CustomType1, CustomType2, String) -> CustomTypeReturn
($func:ident, $Arg1Type:ident, $Arg2Type:ident, $Arg3Type:ident -> $ReturnType:ident) => {
|arg1_dyn: rhai::Dynamic, arg2_dyn: rhai::Dynamic, arg3_str: rhai::ImmutableString| -> rhai::Dynamic {
let arg1_map = arg1_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument 1 not an object map for {}", stringify!($Arg1Type)));
let arg1 = $Arg1Type::from_rhai_map(arg1_map)
.expect(&format!("Failed to convert map to {}", stringify!($Arg1Type)));
let arg2_map = arg2_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument 2 not an object map for {}", stringify!($Arg2Type)));
let arg2 = $Arg2Type::from_rhai_map(arg2_map)
.expect(&format!("Failed to convert map to {}", stringify!($Arg2Type)));
let arg3 = arg3_str.to_string();
let result_struct: $ReturnType = $func(arg1, arg2, arg3);
let result_map = result_struct.to_rhai_map();
rhai::Dynamic::from(result_map)
}
};
// Generic arm for functions like: fn name(Vec<MyCustomStruct>) -> ReturnType
// Where MyCustomStruct derives rhai::CustomType, Clone, 'static
// and implements a method like `from_rhai_map(map: rhai::Map) -> Result<Self, Box<EvalAltResult>>`
($func:ident, Vec<$StructType:ident> -> $ReturnType:ty) => {
|arr: rhai::Array| -> $ReturnType {
let vec_arg: std::vec::Vec<$StructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect("Rhai array element is not an object map");
// Assuming $StructType implements a method to convert from rhai::Map
$StructType::from_rhai_map(map)
.expect(&format!("Failed to convert rhai::Map to {}", stringify!($StructType)))
})
.collect();
$func(vec_arg)
}
};
// Passthrough for functions that are already Rhai-compatible or take no arguments
($func:ident) => {
$func
};
}

193
rhai_wrapper/src/lib.rs.bak Normal file
View File

@@ -0,0 +1,193 @@
//! # Rhai Wrapper
//! Provides a macro to simplify wrapping Rust functions for the Rhai scripting engine.
//!
//! This crate provides a macro and utilities to wrap generic Rust functions so they can be registered with the Rhai scripting engine.
//! It currently supports functions with primitive arguments (i64, f64, String) and return values.
/// Macro to wrap a Rust function for Rhai
///
/// Usage:
/// ```
/// use rhai_wrapper::wrap_for_rhai;
/// use rhai::{Engine, INT};
/// fn add(a: INT, b: INT) -> INT { a + b }
/// let mut engine = Engine::new();
/// engine.register_fn("add", wrap_for_rhai!(add));
/// let result = engine.eval::<INT>("add(2, 3)").unwrap();
/// assert_eq!(result, 5);
/// ```
#[macro_export]
macro_rules! wrap_for_rhai {
// New arm for functions like: fn name(Vec<rhai::INT>) -> rhai::INT
// Usage: wrap_for_rhai!(my_function_name, Vec<INT> -> INT)
($func:ident, Vec<INT> -> INT) => {
|arr: rhai::Array| -> rhai::INT {
let vec_arg: std::vec::Vec<INT> = arr.into_iter()
.map(|x: rhai::Dynamic| x.as_int().expect("Rhai array element is not an INT or could not be converted"))
.collect();
$func(vec_arg)
}
};
// New arm for functions like: fn name(Vec<String>) -> String
// Usage: wrap_for_rhai!(my_function_name, Vec<String> -> String)
($func:ident, Vec<String> -> String) => {
|arr: rhai::Array| -> String {
let vec_arg: std::vec::Vec<String> = arr.into_iter()
.map(|x: rhai::Dynamic| x.into_string().expect("Rhai array element is not a String or could not be converted"))
.collect();
$func(vec_arg)
}
};
// New arm for functions like: fn name(Vec<FLOAT>) -> FLOAT
// Usage: wrap_for_rhai!(my_function_name, Vec<FLOAT> -> FLOAT)
($func:ident, Vec<FLOAT> -> FLOAT) => {
|arr: rhai::Array| -> rhai::FLOAT {
let vec_arg: std::vec::Vec<FLOAT> = arr.into_iter()
.map(|x: rhai::Dynamic| x.as_float().expect("Rhai array element is not a FLOAT or could not be converted"))
.collect();
$func(vec_arg)
}
};
// Specific arms for Vec<CustomStruct> -> Vec<PrimitiveType>
// These must come BEFORE the Vec<CustomIn> -> Vec<CustomOut> arm.
($func:ident, Vec<$InputStructType:ident> -> Vec<INT>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<rhai::INT> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<FLOAT>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<rhai::FLOAT> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<String>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<String> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<bool>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<bool> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
// Arm for Vec<CustomStructIn> -> Vec<CustomStructOut>
// This must come AFTER specific primitive Vec outputs to allow them to match first.
// Requires $InputStructType::from_rhai_map and $OutputStructType::to_rhai_map
($func:ident, Vec<$InputStructType:ident> -> Vec<$OutputStructType:ident>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect("Rhai array element is not an object map for $InputStructType conversion");
$InputStructType::from_rhai_map(map)
.expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<$OutputStructType> = $func(vec_arg);
result_vec.into_iter()
.map(|item: $OutputStructType| item.to_rhai_map().into()) // Convert struct to rhai::Map, then to rhai::Dynamic
.collect::<rhai::Array>()
}
};
// Func(CustomStruct) -> String
($func:ident, $InputStructType:ident -> String) => {
|arg_dyn: rhai::Dynamic| -> String {
let arg_map = arg_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument not an object map for {}", stringify!($InputStructType)));
let arg_struct = $InputStructType::from_rhai_map(arg_map)
.expect(&format!("Failed to convert map to {}", stringify!($InputStructType)));
$func(arg_struct)
}
};
// Func(String, INT, INT) -> CustomReturnType
($func:ident, String, INT, INT -> $ReturnType:ident) => {
|id_str: rhai::ImmutableString, cx_int: rhai::INT, cy_int: rhai::INT| -> rhai::Dynamic {
let id = id_str.to_string();
// cx_int and cy_int are already rhai::INT, matching typical Rust INT type alias for i64
let result_struct: $ReturnType = $func(id, cx_int, cy_int);
let result_map = result_struct.to_rhai_map();
rhai::Dynamic::from(result_map)
}
};
// Func(CustomType1, CustomType2, String) -> CustomTypeReturn
($func:ident, $Arg1Type:ident, $Arg2Type:ident, $Arg3Type:ident -> $ReturnType:ident) => {
|arg1_dyn: rhai::Dynamic, arg2_dyn: rhai::Dynamic, arg3_str: rhai::ImmutableString| -> rhai::Dynamic {
let arg1_map = arg1_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument 1 not an object map for {}", stringify!($Arg1Type)));
let arg1 = $Arg1Type::from_rhai_map(arg1_map)
.expect(&format!("Failed to convert map to {}", stringify!($Arg1Type)));
let arg2_map = arg2_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument 2 not an object map for {}", stringify!($Arg2Type)));
let arg2 = $Arg2Type::from_rhai_map(arg2_map)
.expect(&format!("Failed to convert map to {}", stringify!($Arg2Type)));
let arg3 = arg3_str.to_string();
let result_struct: $ReturnType = $func(arg1, arg2, arg3);
let result_map = result_struct.to_rhai_map();
rhai::Dynamic::from(result_map)
}
};
// Generic arm for functions like: fn name(Vec<MyCustomStruct>) -> ReturnType
// Where MyCustomStruct derives rhai::CustomType, Clone, 'static
// and implements a method like `from_rhai_map(map: rhai::Map) -> Result<Self, Box<EvalAltResult>>`
($func:ident, Vec<$StructType:ident> -> $ReturnType:ty) => {
|arr: rhai::Array| -> $ReturnType {
let vec_arg: std::vec::Vec<$StructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect("Rhai array element is not an object map");
// Assuming $StructType implements a method to convert from rhai::Map
$StructType::from_rhai_map(map)
.expect(&format!("Failed to convert rhai::Map to {}", stringify!($StructType)))
})
.collect();
$func(vec_arg)
}
};
// Passthrough for functions that are already Rhai-compatible or take no arguments
($func:ident) => {
$func
};
}

View File

@@ -0,0 +1,355 @@
//! # Rhai Wrapper
//! Provides a macro to simplify wrapping Rust functions for the Rhai scripting engine.
//!
//! This crate provides a macro and utilities to wrap generic Rust functions so they can be registered with the Rhai scripting engine.
//! It currently supports functions with primitive arguments (i64, f64, String) and return values.
/// Macro to wrap a Rust function for Rhai
///
/// Usage:
/// ```
/// use rhai_wrapper::wrap_for_rhai;
/// use rhai::{Engine, INT};
/// fn add(a: INT, b: INT) -> INT { a + b }
/// let mut engine = Engine::new();
/// engine.register_fn("add", wrap_for_rhai!(add));
/// let result = engine.eval::<INT>("add(2, 3)").unwrap();
/// assert_eq!(result, 5);
/// ```
#[macro_export]
macro_rules! wrap_for_rhai {
// New arm for functions like: fn name(Vec<rhai::INT>) -> rhai::INT
// Usage: wrap_for_rhai!(my_function_name, Vec<INT> -> INT)
($func:ident, Vec<INT> -> INT) => {
|arr: rhai::Array| -> rhai::INT {
let vec_arg: std::vec::Vec<INT> = arr.into_iter()
.map(|x: rhai::Dynamic| x.as_int().expect("Rhai array element is not an INT or could not be converted"))
.collect();
$func(vec_arg)
}
};
// New arm for functions like: fn name(Vec<String>) -> String
// Usage: wrap_for_rhai!(my_function_name, Vec<String> -> String)
($func:ident, Vec<String> -> String) => {
|arr: rhai::Array| -> String {
let vec_arg: std::vec::Vec<String> = arr.into_iter()
.map(|x: rhai::Dynamic| x.into_string().expect("Rhai array element is not a String or could not be converted"))
.collect();
$func(vec_arg)
}
};
// New arm for functions like: fn name(Vec<FLOAT>) -> FLOAT
// Usage: wrap_for_rhai!(my_function_name, Vec<FLOAT> -> FLOAT)
($func:ident, Vec<FLOAT> -> FLOAT) => {
|arr: rhai::Array| -> rhai::FLOAT {
let vec_arg: std::vec::Vec<FLOAT> = arr.into_iter()
.map(|x: rhai::Dynamic| x.as_float().expect("Rhai array element is not a FLOAT or could not be converted"))
.collect();
$func(vec_arg)
}
};
// Specific arms for Vec<CustomStruct> -> Vec<PrimitiveType>
// These must come BEFORE the Vec<CustomIn> -> Vec<CustomOut> arm.
($func:ident, Vec<$InputStructType:ident> -> Vec<INT>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<rhai::INT> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<FLOAT>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<rhai::FLOAT> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<String>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<String> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
($func:ident, Vec<$InputStructType:ident> -> Vec<bool>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>().expect("Rhai array element not map for $InputStructType");
$InputStructType::from_rhai_map(map).expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<bool> = $func(vec_arg);
result_vec.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
};
// Arm for Vec<CustomStructIn> -> Vec<CustomStructOut>
// This must come AFTER specific primitive Vec outputs to allow them to match first.
// Requires $InputStructType::from_rhai_map and $OutputStructType::to_rhai_map
($func:ident, Vec<$InputStructType:ident> -> Vec<$OutputStructType:ident>) => {
|arr: rhai::Array| -> rhai::Array {
let vec_arg: std::vec::Vec<$InputStructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect("Rhai array element is not an object map for $InputStructType conversion");
$InputStructType::from_rhai_map(map)
.expect(&format!("Failed to convert map to {}", stringify!($InputStructType)))
})
.collect();
let result_vec: Vec<$OutputStructType> = $func(vec_arg);
result_vec.into_iter()
.map(|item: $OutputStructType| item.to_rhai_map().into()) // Convert struct to rhai::Map, then to rhai::Dynamic
.collect::<rhai::Array>()
}
};
// Func(CustomStruct) -> String
($func:ident, $InputStructType:ident -> String) => {
|arg_dyn: rhai::Dynamic| -> String {
let arg_map = arg_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument not an object map for {}", stringify!($InputStructType)));
let arg_struct = $InputStructType::from_rhai_map(arg_map)
.expect(&format!("Failed to convert map to {}", stringify!($InputStructType)));
$func(arg_struct)
}
};
// Func(String, INT, INT) -> CustomReturnType
($func:ident, String, INT, INT -> $ReturnType:ident) => {
|id_str: rhai::ImmutableString, cx_int: rhai::INT, cy_int: rhai::INT| -> rhai::Dynamic {
let id = id_str.to_string();
// cx_int and cy_int are already rhai::INT, matching typical Rust INT type alias for i64
let result_struct: $ReturnType = $func(id, cx_int, cy_int);
let result_map = result_struct.to_rhai_map();
rhai::Dynamic::from(result_map)
}
};
// Func(CustomType1, CustomType2, String) -> CustomTypeReturn
($func:ident, $Arg1Type:ident, $Arg2Type:ident, $Arg3Type:ident -> $ReturnType:ident) => {
|arg1_dyn: rhai::Dynamic, arg2_dyn: rhai::Dynamic, arg3_str: rhai::ImmutableString| -> rhai::Dynamic {
let arg1_map = arg1_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument 1 not an object map for {}", stringify!($Arg1Type)));
let arg1 = $Arg1Type::from_rhai_map(arg1_map)
.expect(&format!("Failed to convert map to {}", stringify!($Arg1Type)));
let arg2_map = arg2_dyn.try_cast::<rhai::Map>()
.expect(&format!("Argument 2 not an object map for {}", stringify!($Arg2Type)));
let arg2 = $Arg2Type::from_rhai_map(arg2_map)
.expect(&format!("Failed to convert map to {}", stringify!($Arg2Type)));
let arg3 = arg3_str.to_string();
let result_struct: $ReturnType = $func(arg1, arg2, arg3);
let result_map = result_struct.to_rhai_map();
rhai::Dynamic::from(result_map)
}
};
// Generic arm for functions like: fn name(Vec<MyCustomStruct>) -> ReturnType
// Where MyCustomStruct derives rhai::CustomType, Clone, 'static
// and implements a method like `from_rhai_map(map: rhai::Map) -> Result<Self, Box<EvalAltResult>>`
($func:ident, Vec<$StructType:ident> -> $ReturnType:ty) => {
|arr: rhai::Array| -> $ReturnType {
let vec_arg: std::vec::Vec<$StructType> = arr.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect("Rhai array element is not an object map");
// Assuming $StructType implements a method to convert from rhai::Map
$StructType::from_rhai_map(map)
.expect(&format!("Failed to convert rhai::Map to {}", stringify!($StructType)))
})
.collect();
$func(vec_arg)
}
};
// Passthrough for functions that are already Rhai-compatible or take no arguments
($func:ident) => {
$func
};
// General arm for arbitrary parameter combinations
// This should be matched only if none of the specific arms above match
// Special case for Vec<INT> return type
($func:ident, $($ArgType:ident),* -> Vec<INT>) => {
paste::paste! {
|$([<arg_ $ArgType:lower>]: $crate::__param_type!($ArgType)),*| -> rhai::Array {
$(let [<arg_ $ArgType:lower _converted>] = $crate::__convert_in!($ArgType, [<arg_ $ArgType:lower>]);)*
let result: Vec<INT> = $func($([<arg_ $ArgType:lower _converted>]),*);
result.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
}
};
// Special case for Vec<FLOAT> return type
($func:ident, $($ArgType:ident),* -> Vec<FLOAT>) => {
paste::paste! {
|$([<arg_ $ArgType:lower>]: $crate::__param_type!($ArgType)),*| -> rhai::Array {
$(let [<arg_ $ArgType:lower _converted>] = $crate::__convert_in!($ArgType, [<arg_ $ArgType:lower>]);)*
let result: Vec<FLOAT> = $func($([<arg_ $ArgType:lower _converted>]),*);
result.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>()
}
}
};
// Special case for Vec<String> return type
($func:ident, $($ArgType:ident),* -> Vec<String>) => {
paste::paste! {
|$([<arg_ $ArgType:lower>]: $crate::__param_type!($ArgType)),*| -> rhai::Array {
$(let [<arg_ $ArgType:lower _converted>] = $crate::__convert_in!($ArgType, [<arg_ $ArgType:lower>]);)*
let result: Vec<String> = $func($([<arg_ $ArgType:lower _converted>]),*);
result.into_iter().map(|s| rhai::Dynamic::from(s)).collect::<rhai::Array>()
}
}
};
// Special case for Vec<CustomType> return type
($func:ident, $($ArgType:ident),* -> Vec<$CustomReturnType:ident>) => {
paste::paste! {
|$([<arg_ $ArgType:lower>]: $crate::__param_type!($ArgType)),*| -> rhai::Array {
$(let [<arg_ $ArgType:lower _converted>] = $crate::__convert_in!($ArgType, [<arg_ $ArgType:lower>]);)*
let result: Vec<$CustomReturnType> = $func($([<arg_ $ArgType:lower _converted>]),*);
result.into_iter()
.map(|item| rhai::Dynamic::from(item.to_rhai_map()))
.collect::<rhai::Array>()
}
}
};
// General case for other return types
($func:ident, $($ArgType:ident),* -> $ReturnType:ty) => {
paste::paste! {
|$([<arg_ $ArgType:lower>]: $crate::__param_type!($ArgType)),*| -> $crate::__return_type!($ReturnType) {
$(let [<arg_ $ArgType:lower _converted>] = $crate::__convert_in!($ArgType, [<arg_ $ArgType:lower>]);)*
let result = $func($([<arg_ $ArgType:lower _converted>]),*);
$crate::__convert_out!($ReturnType, result)
}
}
};
}
// Helper macros for the general parameter handling
// The helper macros for argument naming have been removed and replaced with direct paste usage
#[doc(hidden)]
#[macro_export]
macro_rules! __param_type {
// Map Rust types to corresponding Rhai parameter types
(INT) => { rhai::INT };
(FLOAT) => { rhai::FLOAT };
(String) => { rhai::ImmutableString };
(bool) => { bool };
(Vec<$InnerType:ident>) => { rhai::Array };
($CustomType:ident) => { rhai::Dynamic };
}
#[doc(hidden)]
#[macro_export]
macro_rules! __return_type {
// Map return types
(INT) => { rhai::INT };
(FLOAT) => { rhai::FLOAT };
(String) => { String };
(bool) => { bool };
(Vec<$InnerType:ident>) => { rhai::Array }; // Vec<T> always returns rhai::Array
($CustomType:ident) => { rhai::Dynamic };
($ReturnType:ty) => { $ReturnType };
}
#[doc(hidden)]
#[macro_export]
macro_rules! __convert_in {
// Convert Rhai argument types to Rust types
(INT, $arg:ident) => { $arg };
(FLOAT, $arg:ident) => { $arg };
(String, $arg:ident) => { $arg.to_string() };
(bool, $arg:ident) => { $arg };
(Vec<INT>, $arg:ident) => {
$arg.into_iter()
.map(|x: rhai::Dynamic| x.as_int().expect("Rhai array element is not an INT or could not be converted"))
.collect::<Vec<rhai::INT>>()
};
(Vec<FLOAT>, $arg:ident) => {
$arg.into_iter()
.map(|x: rhai::Dynamic| x.as_float().expect("Rhai array element is not a FLOAT or could not be converted"))
.collect::<Vec<rhai::FLOAT>>()
};
(Vec<String>, $arg:ident) => {
$arg.into_iter()
.map(|x: rhai::Dynamic| x.into_string().expect("Rhai array element is not a String or could not be converted"))
.collect::<Vec<String>>()
};
(Vec<$CustomType:ident>, $arg:ident) => {
$arg.into_iter()
.map(|dyn_obj: rhai::Dynamic| {
let map = dyn_obj.try_cast::<rhai::Map>()
.expect(&format!("Rhai array element is not an object map for {}", stringify!($CustomType)));
$CustomType::from_rhai_map(map)
.expect(&format!("Failed to convert map to {}", stringify!($CustomType)))
})
.collect::<Vec<$CustomType>>()
};
($CustomType:ident, $arg:ident) => {
{
let map = $arg.try_cast::<rhai::Map>()
.expect(&format!("Argument not an object map for {}", stringify!($CustomType)));
$CustomType::from_rhai_map(map)
.expect(&format!("Failed to convert map to {}", stringify!($CustomType)))
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __convert_out {
// Process return values
(INT, $result:expr) => { $result };
(FLOAT, $result:expr) => { $result };
(String, $result:expr) => { $result };
(bool, $result:expr) => { $result };
(Vec<INT>, $result:expr) => { $result.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>() };
(Vec<FLOAT>, $result:expr) => { $result.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>() };
(Vec<String>, $result:expr) => { $result.into_iter().map(|s| rhai::Dynamic::from(s)).collect::<rhai::Array>() };
(Vec<bool>, $result:expr) => { $result.into_iter().map(rhai::Dynamic::from).collect::<rhai::Array>() };
(Vec<$CustomType:ident>, $result:expr) => {
$result.into_iter()
.map(|item| rhai::Dynamic::from(item.to_rhai_map()))
.collect::<rhai::Array>()
};
($CustomType:ident, $result:expr) => {
{
let map = $result.to_rhai_map();
rhai::Dynamic::from(map)
}
};
($ReturnType:ty, $result:expr) => { $result };
}