Skip to content

Commit 2dc6558

Browse files
authored
feat!: re-design GetTypeDependencies trait & add GetTypeDependencies derive macro (#369)
# Summary Currently the `GetTypeDependencies` trait struggles with deeply nested container types. For example `HashMap<Result<T,E>,V>` would place bounds of `GetTypeRegistration` on `Result` which quickly falls apart with non-`'static` types like `Ref` and `Mut` which don't have `GetTypeRegistration` implementations. This overhaul introduces the concept of an `Underlying` type allowing us to have two type trees, the `Real` types which we want to end up being registered in the registry, and the `Facade` types which exist purely as trait carriers: ```mermaid graph TD; subgraph facade A1[HashMap<.Val<.T>>] --> A2["`Val<.T>`"] A2 --> T end subgraph underlying B1[HashMap<.T>] --> B2["`T`"] end ``` Thanks to that we can make the two assertions confidently: - I want your REAL types in the script function signature, to be `Typed` and possible to add to the type registry - I don't care if your FACADE types are Also previously, `HashMap<T, Val<T>>` would not register `HashMap<T, T>` correctly at all, meaning we now get better dependency coverage! # Migration Guide - Any types which you implemented `GetTypeDependencies` for manually, will need to specify an underlying type, for non-generic types this will just be `Self`, for types which have recursive registrations, this will be `Self<T1::Underlying, T2::Underlying...>` etc - You can use the derive macro to make this easier instead
1 parent b34d5d4 commit 2dc6558

File tree

9 files changed

+374
-104
lines changed

9 files changed

+374
-104
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use std::{ffi::OsString, path::PathBuf};
44

55
use crate::{
6-
bindings::{ScriptValue, ReflectReference},
7-
docgen::TypedThrough,
6+
bindings::{ReflectReference, ScriptValue},
7+
docgen::TypedThrough, error::InteropError,
88
};
99

1010
use super::{
@@ -77,6 +77,8 @@ impl<T> ArgMeta for Val<T> {}
7777
impl<T> ArgMeta for Ref<'_, T> {}
7878
impl<T> ArgMeta for Mut<'_, T> {}
7979

80+
impl<T> ArgMeta for Result<T, InteropError> {}
81+
8082
impl<T> ArgMeta for Option<T> {
8183
fn default_value() -> Option<ScriptValue> {
8284
Some(ScriptValue::Unit)

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

+19
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,17 @@ where
469469
pub struct Union<T1, T2>(Result<T1, T2>);
470470

471471
impl<T1, T2> Union<T1, T2> {
472+
/// Create a new union with the left value.
473+
pub fn new_left(value: T1) -> Self {
474+
Union(Ok(value))
475+
}
476+
477+
/// Create a new union with the right value.
478+
pub fn new_right(value: T2) -> Self {
479+
Union(Err(value))
480+
}
481+
482+
472483
/// Try interpret the union as the left type
473484
pub fn into_left(self) -> Result<T1, T2> {
474485
match self.0 {
@@ -484,6 +495,14 @@ impl<T1, T2> Union<T1, T2> {
484495
Ok(l) => Err(l),
485496
}
486497
}
498+
499+
/// Map the union to another type
500+
pub fn map_both<U1, U2, F: Fn(T1) -> U1, G: Fn(T2) -> U2>(self, f: F, g: G) -> Union<U1, U2> {
501+
match self.0 {
502+
Ok(t) => Union(Ok(f(t))),
503+
Err(t) => Union(Err(g(t))),
504+
}
505+
}
487506
}
488507

489508
impl<T1: FromScript, T2: FromScript> FromScript for Union<T1, T2>

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

+14-11
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use std::{borrow::Cow, collections::HashMap, ffi::OsString, path::PathBuf};
44
use bevy::reflect::Reflect;
55

6-
use crate::{bindings::{ReflectReference, ScriptValue, WorldGuard}, error::InteropError, private::self_type_dependency_only};
7-
use super::{DynamicScriptFunction, DynamicScriptFunctionMut, Val};
6+
use crate::{bindings::{ReflectReference, ScriptValue, WorldGuard}, error::InteropError};
7+
use super::{DynamicScriptFunction, DynamicScriptFunctionMut, Union, Val};
88

99
/// Converts a value into a [`ScriptValue`].
1010
pub trait IntoScript {
@@ -26,14 +26,13 @@ impl IntoScript for ScriptValue {
2626
}
2727
}
2828

29-
self_type_dependency_only!(ScriptValue);
3029

3130
impl IntoScript for () {
3231
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
3332
Ok(ScriptValue::Unit)
3433
}
3534
}
36-
self_type_dependency_only!(());
35+
3736

3837
impl IntoScript for DynamicScriptFunctionMut {
3938
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
@@ -47,14 +46,12 @@ impl IntoScript for DynamicScriptFunction {
4746
}
4847
}
4948

50-
self_type_dependency_only!(DynamicScriptFunctionMut, DynamicScriptFunction);
5149

5250
impl IntoScript for bool {
5351
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
5452
Ok(ScriptValue::Bool(self))
5553
}
5654
}
57-
self_type_dependency_only!(bool);
5855

5956
macro_rules! impl_into_with_downcast {
6057
($variant:tt as $cast:ty [$($ty:ty),*]) => {
@@ -71,9 +68,7 @@ macro_rules! impl_into_with_downcast {
7168

7269
impl_into_with_downcast!(Integer as i64 [i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize]);
7370
impl_into_with_downcast!(Float as f64 [f32, f64]);
74-
self_type_dependency_only!(
75-
i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize, f32, f64
76-
);
71+
7772

7873
macro_rules! impl_into_stringlike {
7974
($id:ident,[ $(($ty:ty => $conversion:expr)),*]) => {
@@ -99,15 +94,14 @@ impl_into_stringlike!(
9994
]
10095
);
10196

102-
self_type_dependency_only!(String, char, PathBuf, OsString);
10397

10498
impl IntoScript for &'static str {
10599
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
106100
Ok(ScriptValue::String(Cow::Borrowed(self)))
107101
}
108102
}
109103

110-
self_type_dependency_only!(&'static str);
104+
111105

112106
impl IntoScript for ReflectReference {
113107
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
@@ -156,6 +150,15 @@ impl<T: IntoScript, const N: usize> IntoScript for [T; N] {
156150
}
157151
}
158152

153+
impl <T1: IntoScript, T2: IntoScript> IntoScript for Union<T1,T2> {
154+
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
155+
match self.into_left() {
156+
Ok(left) => left.into_script(world),
157+
Err(right) => right.into_script(world),
158+
}
159+
}
160+
}
161+
159162
impl<V: IntoScript> IntoScript for HashMap<String, V> {
160163
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
161164
let mut map = HashMap::new();

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

+23-17
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@ mod test {
1717
use bevy::reflect::{FromReflect, GetTypeRegistration, Reflect, Typed};
1818
use bevy_mod_scripting_derive::script_bindings;
1919

20-
use crate::{
21-
bindings::{
20+
use crate::bindings::{
2221
function::{
2322
from::{Ref, Union, Val},
2423
namespace::IntoNamespace,
2524
script_function::AppScriptFunctionRegistry,
26-
},
27-
script_value::ScriptValue,
28-
},
29-
docgen::typed_through::TypedThrough,
30-
};
25+
}, script_value::ScriptValue
26+
};
3127

3228
use super::arg_meta::{ScriptArgument, ScriptReturn, TypedScriptArgument, TypedScriptReturn};
3329

@@ -137,19 +133,29 @@ mod test {
137133
test_is_valid_arg::<Ref<'_, T>>();
138134
}
139135

136+
fn test_union<T>()
137+
where
138+
T: TypedScriptArgument + TypedScriptReturn,
139+
T::Underlying: FromReflect + Typed + GetTypeRegistration,
140+
for<'a> T::This<'a>: Into<T>,
141+
{
142+
test_is_valid_arg_and_return::<Union<T, T>>();
143+
test_is_valid_arg_and_return::<Union<T, Union<T, T>>>();
144+
}
145+
140146
fn test_array<T, const N: usize>()
141147
where
142-
T: ScriptArgument + ScriptReturn,
143-
T: GetTypeRegistration + FromReflect + TypedThrough + Typed,
148+
T: TypedScriptArgument + TypedScriptReturn + 'static,
149+
T::Underlying: FromReflect + Typed + GetTypeRegistration,
144150
for<'a> T::This<'a>: Into<T>,
145151
{
146152
test_is_valid_arg_and_return::<[T; N]>();
147153
}
148154

149155
fn test_tuple<T>()
150156
where
151-
T: ScriptArgument + ScriptReturn,
152-
T: GetTypeRegistration + FromReflect + TypedThrough + Typed,
157+
T: TypedScriptArgument + TypedScriptReturn + 'static,
158+
T::Underlying: FromReflect + Typed + GetTypeRegistration,
153159
for<'a> T::This<'a>: Into<T>,
154160
{
155161
test_is_valid_arg_and_return::<()>();
@@ -160,26 +166,26 @@ mod test {
160166

161167
fn test_option<T>()
162168
where
163-
T: ScriptArgument + ScriptReturn,
164-
T: GetTypeRegistration + FromReflect + Typed + TypedThrough,
169+
T: TypedScriptArgument + TypedScriptReturn,
170+
T::Underlying: FromReflect + Typed + GetTypeRegistration,
165171
for<'a> T::This<'a>: Into<T>,
166172
{
167173
test_is_valid_arg_and_return::<Option<T>>();
168174
}
169175

170176
fn test_vec<T>()
171177
where
172-
T: ScriptArgument + ScriptReturn,
173-
T: GetTypeRegistration + FromReflect + Typed + TypedThrough,
178+
T: TypedScriptArgument + TypedScriptReturn + 'static,
179+
T::Underlying: FromReflect + Typed + GetTypeRegistration,
174180
for<'a> T::This<'a>: Into<T>,
175181
{
176182
test_is_valid_arg_and_return::<Vec<T>>();
177183
}
178184

179185
fn test_hashmap<V>()
180186
where
181-
V: ScriptArgument + ScriptReturn,
182-
V: GetTypeRegistration + FromReflect + Typed + TypedThrough,
187+
V: TypedScriptArgument + TypedScriptReturn + 'static,
188+
V::Underlying: FromReflect + Typed + GetTypeRegistration + Eq,
183189
for<'a> V::This<'a>: Into<V>,
184190
{
185191
test_is_valid_arg_and_return::<std::collections::HashMap<String, V>>();

0 commit comments

Comments
 (0)