/// Macro to adapt a Rust function taking a specific integer type (e.g., u32) /// to a Rhai function that expects an i64 argument. #[macro_export] macro_rules! adapt_rhai_i64_input_fn { ($rust_fn:path, $rust_int_ty:ty) => { move |context: ::rhai::NativeCallContext, rhai_val: i64| -> Result<_, Box<::rhai::EvalAltResult>> { let rust_val: $rust_int_ty = rhai_val.try_into().map_err(|_e| { Box::new(::rhai::EvalAltResult::ErrorArithmetic( format!("Conversion error for arg in '{}' from i64 to {}", stringify!($rust_fn), stringify!($rust_int_ty)), context.position(), )) })?; Ok($rust_fn(rust_val)) } }; } /// Macro to adapt a Rust instance method (taking self by value, one int arg, returns Self) /// to a Rhai function that expects an i64 for that integer argument. #[macro_export] macro_rules! adapt_rhai_i64_input_method { ($struct_ty:ty, $rust_method_name:ident, $rust_int_ty:ty) => { move |context: ::rhai::NativeCallContext, instance: $struct_ty, rhai_val: i64| -> Result<$struct_ty, Box<::rhai::EvalAltResult>> { let rust_val: $rust_int_ty = rhai_val.try_into().map_err(|_e| { Box::new(::rhai::EvalAltResult::ErrorArithmetic( format!("Conversion error for arg in '{}::{}' from i64 to {}", stringify!($struct_ty), stringify!($rust_method_name), stringify!($rust_int_ty)), context.position(), )) })?; Ok(instance.$rust_method_name(rust_val)) } }; } // --- Rhai Timestamp Helper Functions --- pub mod rhai_timestamp_helpers { use rhai::{INT, EvalAltResult, Position}; use chrono::{DateTime, Utc, TimeZone}; pub fn datetime_to_rhai_timestamp(dt: &DateTime) -> INT { dt.timestamp() } pub fn option_datetime_to_rhai_timestamp(dt_opt: &Option>) -> Option { dt_opt.as_ref().map(datetime_to_rhai_timestamp) } pub fn rhai_timestamp_to_datetime(ts: INT) -> Result, Box> { Utc.timestamp_opt(ts, 0).single() .ok_or_else(|| Box::new(EvalAltResult::ErrorArithmetic(format!("Invalid Unix timestamp: {}", ts), Position::NONE))) } pub fn option_rhai_timestamp_to_datetime(ts_opt: Option) -> Result>, Box> { match ts_opt { Some(ts) => rhai_timestamp_to_datetime(ts).map(Some), None => Ok(None), } } } // --- Macro for Enum Accessors (String Conversion) --- #[macro_export] macro_rules! register_rhai_enum_accessors { ($engine:expr, $struct_type:ty, $field_name:ident, $rhai_name:expr, $to_string_fn:path, $from_string_fn:path) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| -> rhai::ImmutableString { $to_string_fn(&obj.$field_name) }, move |obj: &mut $struct_type, val: rhai::ImmutableString| -> Result<(), Box> { obj.$field_name = $from_string_fn(val.as_str())?; Ok(()) } ); }; } // --- Macro for DateTime Accessors (Unix Timestamp INT) --- #[macro_export] macro_rules! register_rhai_datetime_accessors { ($engine:expr, $struct_type:ty, $field_path:ident, $rhai_name:expr, _required) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| -> rhai::INT { let field_value = &obj.$field_path; $crate::rhai_timestamp_helpers::datetime_to_rhai_timestamp(field_value) }, move |obj: &mut $struct_type, val: rhai::INT| -> Result<(), Box> { obj.$field_path = $crate::rhai_timestamp_helpers::rhai_timestamp_to_datetime(val)?; Ok(()) } ); }; ($engine:expr, $struct_type:ty, base_data.$field_name:ident, $rhai_name:expr, _required) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| -> rhai::INT { let field_value = &obj.base_data.$field_name; $crate::rhai_timestamp_helpers::datetime_to_rhai_timestamp(field_value) }, move |obj: &mut $struct_type, val: rhai::INT| -> Result<(), Box> { obj.base_data.$field_name = $crate::rhai_timestamp_helpers::rhai_timestamp_to_datetime(val)?; Ok(()) } ); }; ($engine:expr, $struct_type:ty, $field_path:ident, $rhai_name:expr) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| -> Option { let field_value = &obj.$field_path; $crate::rhai_timestamp_helpers::option_datetime_to_rhai_timestamp(field_value) }, move |obj: &mut $struct_type, val_opt: Option| -> Result<(), Box> { obj.$field_path = $crate::rhai_timestamp_helpers::option_rhai_timestamp_to_datetime(val_opt)?; Ok(()) } ); }; ($engine:expr, $struct_type:ty, base_data.$field_name:ident, $rhai_name:expr) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| -> Option { let field_value = &obj.base_data.$field_name; $crate::rhai_timestamp_helpers::option_datetime_to_rhai_timestamp(field_value) }, move |obj: &mut $struct_type, val_opt: Option| -> Result<(), Box> { obj.base_data.$field_name = $crate::rhai_timestamp_helpers::option_rhai_timestamp_to_datetime(val_opt)?; Ok(()) } ); }; } // --- Macro for Vec Accessors --- #[macro_export] macro_rules! register_rhai_vec_string_accessors { ($engine:expr, $struct_type:ty, $field_name:ident, $rhai_name:expr) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| -> rhai::Array { obj.$field_name.iter().map(|s| rhai::Dynamic::from(rhai::ImmutableString::from(s.as_str()))).collect() }, move |obj: &mut $struct_type, val: rhai::Array| { obj.$field_name = val.into_iter().map(|d| d.into_string().unwrap_or_default()).collect(); } ); }; } // --- Macro for Generic Field Accessors (Example: ImmutableString) --- #[macro_export] macro_rules! register_rhai_field_accessors { ($engine:expr, $struct_type:ty, $field_name:ident, $rhai_name:expr) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| obj.$field_name.clone(), // Assuming cloneable and directly Rhai compatible move |obj: &mut $struct_type, val: rhai::Dynamic| { // Or specific type like ImmutableString // This part would need more specific handling based on expected type // For example, if it's always ImmutableString: // if let Ok(s) = val.into_immutable_string() { obj.$field_name = s.into_owned(); } // For now, let's assume it's a type that can be directly assigned from Dynamic if Dynamic holds the right type // This is a simplification; real use might need obj.$field_name = val.try_cast().unwrap_or_default(); // However, register_get_set usually infers setter type from getter type. // If getter is T, setter is fn(&mut S, T) // So if getter is |obj| obj.field.clone() -> String, setter should be |obj, val: String| // Let's assume string for now if using ImmutableString for Rhai if let Ok(s_val) = val.into_immutable_string() { obj.$field_name = s_val.into(); // Assumes field_name is String } else { // Handle error or default eprintln!("Failed to cast for field {}", $rhai_name); } } ); }; ($engine:expr, $struct_type:ty, $field_name:ident, $rhai_name:expr, $rhai_type:ty) => { $engine.register_get_set( $rhai_name, move |obj: &mut $struct_type| obj.$field_name.clone(), move |obj: &mut $struct_type, val: $rhai_type| { obj.$field_name = val; } ); }; }