Skip to content

Commit 5656c38

Browse files
committed
add function introspection methods
1 parent db8a7c3 commit 5656c38

File tree

21 files changed

+843
-287
lines changed

21 files changed

+843
-287
lines changed

crates/bevy_mod_scripting_core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ parking_lot = "0.12.1"
3939
dashmap = "6"
4040
smallvec = "1.11"
4141
itertools = "0.13"
42+
derivative = "2.2"
4243

4344
[dev-dependencies]
4445
test_utils = { workspace = true }

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

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! Trait implementations to help with function dispatch.
2+
3+
use std::{ffi::OsString, path::PathBuf};
4+
5+
use crate::bindings::{script_value::ScriptValue, ReflectReference};
6+
7+
use super::{
8+
from::{FromScript, Mut, Ref, Val},
9+
into::IntoScript,
10+
script_function::{DynamicScriptFunction, DynamicScriptFunctionMut, FunctionCallContext},
11+
type_dependencies::GetTypeDependencies,
12+
};
13+
14+
/// Marker trait for types that can be used as arguments to a script function.
15+
pub trait ScriptArgument: ArgMeta + FromScript + GetTypeDependencies {}
16+
impl<T: ArgMeta + FromScript + GetTypeDependencies> ScriptArgument for T {}
17+
18+
/// Marker trait for types that can be used as return values from a script function.
19+
pub trait ScriptReturn: IntoScript + GetTypeDependencies {}
20+
impl<T: IntoScript + GetTypeDependencies> ScriptReturn for T {}
21+
22+
/// Describes an argument to a script function. Provides necessary information for the function to handle dispatch.
23+
pub trait ArgMeta {
24+
fn default_value() -> Option<ScriptValue> {
25+
None
26+
}
27+
}
28+
29+
impl ArgMeta for ScriptValue {}
30+
31+
macro_rules! impl_arg_info {
32+
($($ty:ty),*) => {
33+
$(
34+
impl ArgMeta for $ty {}
35+
)*
36+
};
37+
}
38+
39+
impl_arg_info!(bool, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64, usize, isize);
40+
41+
impl_arg_info!(String, PathBuf, OsString);
42+
43+
impl_arg_info!(char);
44+
45+
impl_arg_info!(ReflectReference);
46+
47+
impl<T> ArgMeta for Val<T> {}
48+
impl<T> ArgMeta for Ref<'_, T> {}
49+
impl<T> ArgMeta for Mut<'_, T> {}
50+
51+
impl<T> ArgMeta for Option<T> {
52+
fn default_value() -> Option<ScriptValue> {
53+
Some(ScriptValue::Unit)
54+
}
55+
}
56+
57+
impl<T> ArgMeta for Vec<T> {}
58+
impl<T, const N: usize> ArgMeta for [T; N] {}
59+
60+
impl<K, V> ArgMeta for std::collections::HashMap<K, V> {}
61+
62+
impl_arg_info!(DynamicScriptFunction, DynamicScriptFunctionMut);
63+
64+
impl ArgMeta for () {
65+
fn default_value() -> Option<ScriptValue> {
66+
Some(ScriptValue::Unit)
67+
}
68+
}
69+
impl<T> ArgMeta for (T,) {}
70+
impl<T1, T2> ArgMeta for (T1, T2) {}
71+
impl<T1, T2, T3> ArgMeta for (T1, T2, T3) {}
72+
impl<T1, T2, T3, T4> ArgMeta for (T1, T2, T3, T4) {}
73+
impl<T1, T2, T3, T4, T5> ArgMeta for (T1, T2, T3, T4, T5) {}
74+
impl<T1, T2, T3, T4, T5, T6> ArgMeta for (T1, T2, T3, T4, T5, T6) {}
75+
impl<T1, T2, T3, T4, T5, T6, T7> ArgMeta for (T1, T2, T3, T4, T5, T6, T7) {}
76+
impl<T1, T2, T3, T4, T5, T6, T7, T8> ArgMeta for (T1, T2, T3, T4, T5, T6, T7, T8) {}
77+
impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> ArgMeta for (T1, T2, T3, T4, T5, T6, T7, T8, T9) {}
78+
impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ArgMeta
79+
for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
80+
{
81+
}
82+
impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> ArgMeta
83+
for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
84+
{
85+
}
86+
impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> ArgMeta
87+
for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
88+
{
89+
}
90+
impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> ArgMeta
91+
for (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)
92+
{
93+
}
94+
95+
impl ArgMeta for FunctionCallContext {}

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,27 @@ impl FromScript for DynamicScriptFunction {
421421
}
422422
}
423423
}
424+
425+
impl<V> FromScript for std::collections::HashMap<String, V>
426+
where
427+
V: FromScript + 'static,
428+
for<'w> V::This<'w>: Into<V>,
429+
{
430+
type This<'w> = Self;
431+
432+
fn from_script(value: ScriptValue, world: WorldGuard) -> Result<Self, InteropError> {
433+
match value {
434+
ScriptValue::Map(map) => {
435+
let mut hashmap = std::collections::HashMap::new();
436+
for (key, value) in map {
437+
hashmap.insert(key, V::from_script(value, world.clone())?.into());
438+
}
439+
Ok(hashmap)
440+
}
441+
_ => Err(InteropError::value_mismatch(
442+
std::any::TypeId::of::<std::collections::HashMap<String, V>>(),
443+
value,
444+
)),
445+
}
446+
}
447+
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{borrow::Cow, ffi::OsString, path::PathBuf};
1+
use std::{borrow::Cow, collections::HashMap, ffi::OsString, path::PathBuf};
22

33
use bevy::reflect::Reflect;
44

@@ -153,6 +153,16 @@ impl<T: IntoScript, const N: usize> IntoScript for [T; N] {
153153
}
154154
}
155155

156+
impl<V: IntoScript> IntoScript for HashMap<String, V> {
157+
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
158+
let mut map = HashMap::new();
159+
for (key, value) in self {
160+
map.insert(key, value.into_script(world.clone())?);
161+
}
162+
Ok(ScriptValue::Map(map))
163+
}
164+
}
165+
156166
impl IntoScript for InteropError {
157167
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
158168
Ok(ScriptValue::Error(self))

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{ffi::OsString, path::PathBuf};
1+
use std::{borrow::Cow, ffi::OsString, path::PathBuf};
22

33
use bevy::reflect::{Access, PartialReflect};
44

@@ -90,6 +90,13 @@ fn into_script_ref(
9090
to : bool => return downcast_into_value!(r, bool).into_script(world),
9191
tp : char => return downcast_into_value!(r, char).into_script(world),
9292
tq : String => return downcast_into_value!(r, String).clone().into_script(world),
93+
tcs: Cow<'static, str> => match r.try_downcast_ref::<Cow<'static, str>>() {
94+
Some(cow) => return Ok(ScriptValue::String(cow.clone())),
95+
None => return Err(InteropError::type_mismatch(
96+
std::any::TypeId::of::<Cow<str>>(),
97+
r.get_represented_type_info().map(|i| i.type_id()),
98+
)),
99+
},
93100
tr : PathBuf => return downcast_into_value!(r, PathBuf).clone().into_script(world),
94101
ts : OsString=> return downcast_into_value!(r, OsString).clone().into_script(world),
95102
tn : () => return Ok(ScriptValue::Unit)
Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,139 @@
1-
pub mod arg_info;
1+
pub mod arg_meta;
22
pub mod from;
33
pub mod from_ref;
44
pub mod into;
55
pub mod into_ref;
66
pub mod namespace;
77
pub mod script_function;
8+
pub mod type_dependencies;
9+
10+
#[cfg(test)]
11+
#[allow(dead_code)]
12+
mod test {
13+
use bevy::reflect::{FromReflect, GetTypeRegistration, Typed};
14+
15+
use crate::{
16+
bindings::function::from::{Ref, Val},
17+
error::InteropError,
18+
};
19+
20+
use super::arg_meta::{ScriptArgument, ScriptReturn};
21+
22+
fn test_is_valid_return<T: ScriptReturn>() {}
23+
fn test_is_valid_arg<T: ScriptArgument>() {}
24+
fn test_is_valid_arg_and_return<T: ScriptArgument + ScriptReturn>() {}
25+
26+
#[test]
27+
fn primitives_are_valid_args() {
28+
test_is_valid_arg_and_return::<bool>();
29+
test_is_valid_arg_and_return::<i8>();
30+
test_is_valid_arg_and_return::<i16>();
31+
test_is_valid_arg_and_return::<i32>();
32+
test_is_valid_arg_and_return::<i64>();
33+
test_is_valid_arg_and_return::<i128>();
34+
test_is_valid_arg_and_return::<u8>();
35+
test_is_valid_arg_and_return::<u16>();
36+
test_is_valid_arg_and_return::<u32>();
37+
test_is_valid_arg_and_return::<u64>();
38+
test_is_valid_arg_and_return::<u128>();
39+
test_is_valid_arg_and_return::<f32>();
40+
test_is_valid_arg_and_return::<f64>();
41+
test_is_valid_arg_and_return::<usize>();
42+
test_is_valid_arg_and_return::<isize>();
43+
}
44+
45+
#[test]
46+
fn strings_are_valid_args() {
47+
test_is_valid_arg_and_return::<String>();
48+
test_is_valid_arg_and_return::<std::path::PathBuf>();
49+
test_is_valid_arg_and_return::<std::ffi::OsString>();
50+
test_is_valid_arg_and_return::<char>();
51+
}
52+
53+
#[test]
54+
fn composites_are_valid_args() {
55+
fn test_val<T>()
56+
where
57+
T: ScriptArgument + ScriptReturn,
58+
T: GetTypeRegistration + FromReflect,
59+
{
60+
test_is_valid_arg_and_return::<Val<T>>();
61+
}
62+
63+
fn test_ref<T>()
64+
where
65+
T: ScriptArgument,
66+
T: GetTypeRegistration + FromReflect + Typed,
67+
{
68+
test_is_valid_arg::<Ref<'_, T>>();
69+
}
70+
71+
fn test_mut<T>()
72+
where
73+
T: ScriptArgument,
74+
T: GetTypeRegistration + FromReflect + Typed,
75+
{
76+
test_is_valid_arg::<Ref<'_, T>>();
77+
}
78+
79+
test_is_valid_return::<InteropError>();
80+
81+
fn test_array<T, const N: usize>()
82+
where
83+
T: ScriptArgument + ScriptReturn,
84+
T: GetTypeRegistration + FromReflect + Typed,
85+
for<'a> T::This<'a>: Into<T>,
86+
{
87+
test_is_valid_arg_and_return::<[T; N]>();
88+
}
89+
90+
fn test_tuple<T>()
91+
where
92+
T: ScriptArgument + ScriptReturn,
93+
T: GetTypeRegistration + FromReflect + Typed,
94+
for<'a> T::This<'a>: Into<T>,
95+
{
96+
test_is_valid_arg_and_return::<()>();
97+
test_is_valid_return::<(T,)>();
98+
test_is_valid_return::<(T, T)>();
99+
test_is_valid_return::<(T, T, T, T, T, T, T, T, T, T)>();
100+
}
101+
102+
fn test_option<T>()
103+
where
104+
T: ScriptArgument + ScriptReturn,
105+
T: GetTypeRegistration + FromReflect + Typed,
106+
for<'a> T::This<'a>: Into<T>,
107+
{
108+
test_is_valid_arg_and_return::<Option<T>>();
109+
}
110+
111+
fn test_vec<T>()
112+
where
113+
T: ScriptArgument + ScriptReturn,
114+
T: GetTypeRegistration + FromReflect + Typed,
115+
for<'a> T::This<'a>: Into<T>,
116+
{
117+
test_is_valid_arg_and_return::<Vec<T>>();
118+
}
119+
120+
fn test_hashmap<V>()
121+
where
122+
V: ScriptArgument + ScriptReturn,
123+
V: GetTypeRegistration + FromReflect + Typed,
124+
for<'a> V::This<'a>: Into<V>,
125+
{
126+
test_is_valid_arg_and_return::<std::collections::HashMap<String, V>>();
127+
}
128+
}
129+
130+
#[test]
131+
fn test_dynamic_functions() {
132+
test_is_valid_arg_and_return::<
133+
crate::bindings::function::script_function::DynamicScriptFunction,
134+
>();
135+
test_is_valid_arg_and_return::<
136+
crate::bindings::function::script_function::DynamicScriptFunctionMut,
137+
>();
138+
}
139+
}

0 commit comments

Comments
 (0)