create macros for generating rhai wrappers and add tests
This commit is contained in:
210
rhai_wrapper/src/lib.rs
Normal file
210
rhai_wrapper/src/lib.rs
Normal 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
193
rhai_wrapper/src/lib.rs.bak
Normal 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
|
||||
};
|
||||
}
|
355
rhai_wrapper/src/lib.rs.bak2
Normal file
355
rhai_wrapper/src/lib.rs.bak2
Normal 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 };
|
||||
}
|
Reference in New Issue
Block a user