From a0a2fcf20c7268cd01a86d4c87a89635e5d5ebbd Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 27 Jan 2025 23:48:49 +0000 Subject: [PATCH] feat: add hashmap support --- .../src/bindings/script_value.rs | 34 +++++++++++++++---- .../hashmap/can_pass_and_return_hashmap.lua | 7 ++++ .../src/bindings/script_value.rs | 22 ++++++++++-- .../hashmap/can_pass_and_return_hashmap.rhai | 7 ++++ .../src/test_functions.rs | 5 +-- 5 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 crates/languages/bevy_mod_scripting_lua/tests/data/hashmap/can_pass_and_return_hashmap.lua create mode 100644 crates/languages/bevy_mod_scripting_rhai/tests/data/hashmap/can_pass_and_return_hashmap.rhai diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs index bddced885f..8de5d1d668 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs @@ -38,7 +38,7 @@ impl From for ScriptValue { } impl FromLua for LuaScriptValue { - fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result { + fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result { Ok(match value { Value::Nil => ScriptValue::Unit, Value::Boolean(b) => ScriptValue::Bool(b), @@ -50,12 +50,34 @@ impl FromLua for LuaScriptValue { Value::Number(n) => ScriptValue::Float(n), Value::String(s) => ScriptValue::String(s.to_str()?.to_owned().into()), Value::Table(table) => { - let mut vec = Vec::with_capacity(table.len()? as usize); - for i in table.sequence_values() { - let v: LuaScriptValue = i?; - vec.push(v.into()); + // check the key types, if strings then it's a map + let mut iter = table.pairs::(); + + match iter.next() { + Some(v) => { + let (k, v) = v?; + // if the key is a string, then it's a map + if k.is_string() { + let mut map = HashMap::new(); + map.insert(k.to_string()?, v.into()); + for pair in iter { + let (k, v) = pair?; + let str_: String = String::from_lua(k, lua)?; + map.insert(str_, v.into()); + } + return Ok(LuaScriptValue::from(ScriptValue::Map(map))); + } else { + // if the key is an integer, then it's a list + let mut vec = Vec::with_capacity(table.len()? as usize); + vec.push(v.into()); + for pair in iter { + vec.push(pair?.1.into()); + } + return Ok(LuaScriptValue::from(ScriptValue::List(vec))); + } + } + None => return Ok(LuaScriptValue::from(ScriptValue::List(vec![]))), } - ScriptValue::List(vec) } // Value::Thread(thread) => todo!(), Value::UserData(ud) => { diff --git a/crates/languages/bevy_mod_scripting_lua/tests/data/hashmap/can_pass_and_return_hashmap.lua b/crates/languages/bevy_mod_scripting_lua/tests/data/hashmap/can_pass_and_return_hashmap.lua new file mode 100644 index 0000000000..68a84b49a9 --- /dev/null +++ b/crates/languages/bevy_mod_scripting_lua/tests/data/hashmap/can_pass_and_return_hashmap.lua @@ -0,0 +1,7 @@ +local map = make_hashmap({ + key1 = 2, + key2 = 3, +}) + +assert(map["key1"] == 2, "map[\"key1\"] should be 2") +assert(map["key2"] == 3, "map[\"key2\"] should be 3") \ No newline at end of file diff --git a/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs index 3992e00e72..f3d4a8ad12 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs @@ -129,7 +129,7 @@ pub trait FromDynamic: Sized { fn from_dynamic(dynamic: Dynamic) -> Result>; } -#[allow(clippy::unwrap_used, clippy::todo)] +#[allow(clippy::unwrap_used)] impl FromDynamic for ScriptValue { fn from_dynamic(dynamic: Dynamic) -> Result> { match dynamic { @@ -140,6 +140,18 @@ impl FromDynamic for ScriptValue { d if d.is_string() => Ok(ScriptValue::String( d.into_immutable_string().unwrap().to_string().into(), )), + mut d if d.is_map() => Ok(ScriptValue::Map( + d.as_map_mut() + .map_err(|_| { + Box::new(EvalAltResult::ErrorSystem( + "FromDynamic".to_string(), + InteropError::invariant("d is proved to be a map").into(), + )) + })? + .iter() + .map(|(k, v)| Ok((k.to_string(), ScriptValue::from_dynamic(v.clone())?))) + .collect::>>()?, + )), d if d.is_array() => Ok(ScriptValue::List( d.into_array() .map_err(|_| InteropError::invariant("d is proved to be an array"))? @@ -151,7 +163,13 @@ impl FromDynamic for ScriptValue { if let Some(v) = d.try_cast::() { Ok(ScriptValue::Reference(v.0)) } else { - todo!("from conversion not implemented yet") + Err(Box::new(EvalAltResult::ErrorSystem( + "FromDynamic".to_string(), + Box::new(InteropError::impossible_conversion(std::any::TypeId::of::< + ScriptValue, + >( + ))), + ))) } } } diff --git a/crates/languages/bevy_mod_scripting_rhai/tests/data/hashmap/can_pass_and_return_hashmap.rhai b/crates/languages/bevy_mod_scripting_rhai/tests/data/hashmap/can_pass_and_return_hashmap.rhai new file mode 100644 index 0000000000..60b45a3da4 --- /dev/null +++ b/crates/languages/bevy_mod_scripting_rhai/tests/data/hashmap/can_pass_and_return_hashmap.rhai @@ -0,0 +1,7 @@ +let my_map = make_hashmap.call(#{ + key1: 2, + key2: 3, +}); + +assert(my_map["key1"] == 2, "map[\"key1\"] should be 2"); +assert(my_map["key2"] == 3, "map[\"key2\"] should be 3"); \ No newline at end of file diff --git a/crates/script_integration_test_harness/src/test_functions.rs b/crates/script_integration_test_harness/src/test_functions.rs index 805fa2b833..4c52fee834 100644 --- a/crates/script_integration_test_harness/src/test_functions.rs +++ b/crates/script_integration_test_harness/src/test_functions.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use bevy::{ app::App, @@ -102,5 +102,6 @@ pub fn register_test_functions(world: &mut App) { ); NamespaceBuilder::::new_unregistered(world) - .register("global_hello_world", || Ok("hi!")); + .register("global_hello_world", || Ok("hi!")) + .register("make_hashmap", |map: HashMap| map); }