Skip to content

Commit a5841da

Browse files
authored
feat: support setting hashmaps via reflection (#330)
1 parent 11fdba2 commit a5841da

File tree

5 files changed

+55
-4
lines changed

5 files changed

+55
-4
lines changed

crates/bevy_mod_scripting_core/src/bindings/function/from_ref.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
33
use std::{any::TypeId, ffi::OsString, path::PathBuf};
44

5-
use bevy::reflect::{DynamicEnum, DynamicList, DynamicTuple, DynamicVariant, PartialReflect};
5+
use bevy::reflect::{
6+
DynamicEnum, DynamicList, DynamicMap, DynamicTuple, DynamicVariant, Map, PartialReflect,
7+
};
68

79
use crate::{
810
bindings::{function::from::FromScript, WorldGuard},
@@ -100,6 +102,23 @@ impl FromScriptRef for Box<dyn PartialReflect> {
100102
}
101103
}
102104

105+
if let Some((key_type, val_type)) = type_info.map_inner_types() {
106+
if let ScriptValue::Map(map) = value {
107+
let mut dynamic_map = DynamicMap::default();
108+
for (key, val) in map {
109+
let key = Self::from_script_ref(
110+
key_type,
111+
ScriptValue::String(key.into()),
112+
world.clone(),
113+
)?;
114+
let val = Self::from_script_ref(val_type, val, world.clone())?;
115+
dynamic_map.insert_boxed(key, val);
116+
}
117+
dynamic_map.set_represented_type(Some(type_info));
118+
return Ok(Box::new(dynamic_map));
119+
}
120+
}
121+
103122
match value {
104123
ScriptValue::Reference(reflect_reference) => reflect_reference.to_owned_value(world),
105124
value => Err(InteropError::value_mismatch(target, value)),

crates/bevy_mod_scripting_core/src/reflection_extensions.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ impl<T: PartialReflect + ?Sized> PartialReflectExt for T {
419419

420420
/// Extension trait for TypeInfos providing additional functionality for working with type information.
421421
pub trait TypeInfoExtensions {
422+
/// Returns the inner type of the map if the type is a map, otherwise
423+
fn map_inner_types(&self) -> Option<(TypeId, TypeId)>;
422424
/// Returns the inner type of the list if the type is a list, otherwise None.
423425
fn list_inner_type(&self) -> Option<TypeId>;
424426
/// Returns true if the type is a list.
@@ -452,6 +454,11 @@ impl TypeInfoExtensions for TypeInfo {
452454
Some(self.as_list().ok()?.item_ty().id())
453455
}
454456

457+
fn map_inner_types(&self) -> Option<(TypeId, TypeId)> {
458+
let map = self.as_map().ok()?;
459+
Some((map.key_ty().id(), map.value_ty().id()))
460+
}
461+
455462
fn is_type(&self, crate_name: Option<&str>, type_ident: &str) -> bool {
456463
self.type_path_table().ident() == Some(type_ident)
457464
&& self.type_path_table().crate_name() == crate_name

crates/languages/bevy_mod_scripting_lua/tests/data/set/set_primitives_works.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,29 @@ resource.bool = true
66
resource.int = 42
77
resource.float = 3.0
88
resource.vec_usize = { 1, 2 }
9+
resource.string_map = { foo = "hello", zoo = "world" }
910

1011
assert(resource.string == "Hello, World!", "Expected 'Hello, World!', got " .. resource.string)
1112
assert(resource.bool == true, "Expected true, got " .. tostring(resource.bool))
1213
assert(resource.int == 42, "Expected 42, got " .. resource.int)
1314
assert(resource.float == 3.0, "Expected 3.14, got " .. resource.float)
1415
assert(resource.vec_usize[1] == 1, "Expected 1, got " .. resource.vec_usize[1])
16+
assert(resource.string_map:len() == 2, "Expected 2, got " .. resource.string_map:len())
17+
-- assert(resource.string_map["foo"] == "hello", "Expected 'hello', got " .. resource.string_map["foo"])
18+
-- assert(resource.string_map["zoo"] == "world", "Expected 'world', got " .. resource.string_map["zoo"])
1519

1620
resource.string = "Goodbye, World!"
1721
resource.bool = false
1822
resource.int = 24
1923
resource.float = 1.0
2024
resource.vec_usize = { 3, 4 }
25+
resource.string_map = { foo = "goodbye", zoo = "world" }
2126

2227
assert(resource.string == "Goodbye, World!", "Expected 'Goodbye, World!', got " .. resource.string)
2328
assert(resource.bool == false, "Expected false, got " .. tostring(resource.bool))
2429
assert(resource.int == 24, "Expected 24, got " .. resource.int)
2530
assert(resource.float == 1.0, "Expected 1.41, got " .. resource.float)
26-
assert(resource.vec_usize[1] == 3, "Expected 3, got " .. resource.vec_usize[1])
27-
31+
assert(resource.string_map:len() == 2, "Expected 2, got " .. resource.string_map:len())
32+
-- assert(resource.string_map["foo"] == "goodbye", "Expected 'goodbye', got " .. resource.string_map["foo"])
33+
-- assert(resource.string_map["zoo"] == "world", "Expected 'world', got " .. resource.string_map["zoo"])
2834

crates/languages/bevy_mod_scripting_rhai/tests/data/set/set_primitives_works.rhai

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,36 @@ resource.bool = true;
66
resource.int = 42;
77
resource.float = 3.0;
88
resource.vec_usize = [ 1, 2 ];
9+
resource.string_map = #{
10+
foo: "string1",
11+
zoo: "string2"
12+
};
913

1014
assert(resource.string == "Hello, World!", "Expected 'Hello, World!', got " + resource.string);
1115
assert(resource.bool == true, "Expected true, got " + resource.bool);
1216
assert(resource.int == 42, "Expected 42, got " + resource.int);
1317
assert(resource.float == 3.0, "Expected 3.14, got " + resource.float);
1418
assert(resource.vec_usize[0] == 1, "Expected 1, got " + resource.vec_usize[1]);
19+
assert(resource.string_map.len.call() == 2, "Expected 2, got " + resource.string_map.len.call());
20+
// assert(resource.string_map.foo == "string1", "Expected 'string1', got " + resource.string_map.foo);
21+
// assert(resource.string_map.zoo == "string2", "Expected 'string2', got " + resource.string_map.zoo);
1522

1623
resource.string = "Goodbye, World!";
1724
resource.bool = false;
1825
resource.int = 24;
1926
resource.float = 1.0;
2027
resource.vec_usize = [ 3, 4 ];
28+
resource.string_map = #{
29+
foo: "goodbye",
30+
zoo: "world"
31+
};
2132

2233
assert(resource.string == "Goodbye, World!", "Expected 'Goodbye, World!', got " + resource.string);
2334
assert(resource.bool == false, "Expected false, got " + resource.bool);
2435
assert(resource.int == 24, "Expected 24, got " + resource.int);
2536
assert(resource.float == 1.0, "Expected 1.41, got " + resource.float);
2637
assert(resource.vec_usize[0] == 3, "Expected 3, got " + resource.vec_usize[1]);
27-
38+
assert(resource.string_map.len.call() == 2, "Expected 2, got " + resource.string_map.len.call());
39+
// assert(resource.string_map.foo == "goodbye", "Expected 'goodbye', got " + resource.string_map.foo);
40+
// assert(resource.string_map.zoo == "world", "Expected 'world', got " + resource.string_map.zoo);
2841

crates/testing_crates/test_utils/src/test_data.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::alloc::Layout;
2+
use std::collections::HashMap;
23

34
use bevy::asset::AssetPlugin;
45
use bevy::ecs::{component::*, world::World};
@@ -124,6 +125,7 @@ pub struct TestResourceWithVariousFields {
124125
pub float: f32,
125126
pub bool: bool,
126127
pub vec_usize: Vec<usize>,
128+
pub string_map: HashMap<String, String>,
127129
}
128130

129131
impl TestResourceWithVariousFields {
@@ -135,6 +137,10 @@ impl TestResourceWithVariousFields {
135137
float: 69.0,
136138
bool: true,
137139
vec_usize: vec![1, 2, 3, 4, 5],
140+
string_map: vec![("foo", "bar"), ("zoo", "zed")]
141+
.into_iter()
142+
.map(|(a, b)| (a.to_owned(), b.to_owned()))
143+
.collect(),
138144
}
139145
}
140146
}

0 commit comments

Comments
 (0)