Skip to content

Commit b36de62

Browse files
authored
feat(ladfile): improve globals in LAD format (#372)
# Summary For now globals in the ladfile didn't really support values other than "static" instances, i.e. you could only place a `LadTypeId` in the global namespace, meaning it would have to be a pure type, and not something like `Vec<T>`. This change allows anything in the globals that would be allowed in function arguments. This also improves the formatting in the globals section: - Types mentioned link to their detail sections - Static instances are separated from non-static instances, to make it easier to read - Descriptive text is provided ![image](https://github.com/user-attachments/assets/1e9d4a95-5500-4001-8ef2-7372ae3dc56b)
1 parent 5169e0c commit b36de62

File tree

13 files changed

+474
-170
lines changed

13 files changed

+474
-170
lines changed

crates/bevy_mod_scripting_core/src/bindings/globals/core.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{collections::HashMap, sync::Arc};
55
use bevy::{app::Plugin, ecs::reflect::AppTypeRegistry};
66
use bevy_mod_scripting_derive::script_globals;
77

8-
use crate::{bindings::{function::from::{Union, Val}, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration, WorldGuard}, error::InteropError};
8+
use crate::{bindings::{function::from::{Union, Val}, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration, WorldGuard}, docgen::into_through_type_info, error::InteropError};
99

1010
use super::AppScriptGlobalsRegistry;
1111

@@ -38,9 +38,10 @@ fn register_static_core_globals(world: &mut bevy::ecs::world::World) {
3838

3939
if let Some(global_name) = registration.type_info().type_path_table().ident() {
4040
let documentation = "A reference to the type, allowing you to call static methods.";
41+
let type_info = registration.type_info();
4142
global_registry.register_static_documented_dynamic(
4243
registration.type_id(),
43-
None,
44+
into_through_type_info(type_info),
4445
global_name.into(),
4546
documentation.into(),
4647
);

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

+11-11
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use super::{
55
script_value::ScriptValue,
66
WorldGuard,
77
};
8-
use crate::{docgen::typed_through::ThroughTypeInfo, error::InteropError};
9-
use bevy::{ecs::system::Resource, utils::hashbrown::HashMap};
8+
use crate::{docgen::{into_through_type_info, typed_through::ThroughTypeInfo}, error::InteropError};
9+
use bevy::{ecs::system::Resource, reflect::Typed, utils::hashbrown::HashMap};
1010
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
1111
use std::{any::TypeId, borrow::Cow, sync::Arc};
1212

@@ -45,7 +45,7 @@ pub struct ScriptGlobal {
4545
/// The type ID of the global variable.
4646
pub type_id: TypeId,
4747
/// Rich type information the global variable.
48-
pub type_information: Option<ThroughTypeInfo>,
48+
pub type_information: ThroughTypeInfo,
4949
}
5050

5151
/// A registry of global variables that can be exposed to scripts.
@@ -96,7 +96,7 @@ impl ScriptGlobalsRegistry {
9696

9797
/// Inserts a global into the registry, returns the previous value if it existed
9898
pub fn register<
99-
T: ScriptReturn + 'static,
99+
T: ScriptReturn + 'static + Typed,
100100
F: Fn(WorldGuard) -> Result<T, InteropError> + 'static + Send + Sync,
101101
>(
102102
&mut self,
@@ -109,7 +109,7 @@ impl ScriptGlobalsRegistry {
109109
maker: Some(Self::type_erase_maker(maker)),
110110
documentation: None,
111111
type_id: TypeId::of::<T>(),
112-
type_information: None,
112+
type_information: into_through_type_info(T::type_info()),
113113
},
114114
)
115115
}
@@ -132,28 +132,28 @@ impl ScriptGlobalsRegistry {
132132
maker: Some(Self::type_erase_maker(maker)),
133133
documentation: Some(documentation.into()),
134134
type_id: TypeId::of::<T>(),
135-
type_information: Some(T::through_type_info()),
135+
type_information: T::through_type_info(),
136136
},
137137
)
138138
}
139139

140140
/// Registers a static global into the registry.
141-
pub fn register_static<T: 'static>(&mut self, name: Cow<'static, str>) {
141+
pub fn register_static<T: 'static + Typed>(&mut self, name: Cow<'static, str>) {
142142
self.globals.insert(
143143
name,
144144
ScriptGlobal {
145145
maker: None,
146146
documentation: None,
147147
type_id: TypeId::of::<T>(),
148-
type_information: None,
148+
type_information: into_through_type_info(T::type_info()),
149149
},
150150
);
151151
}
152152

153153
/// Registers a static global into the registry.
154154
///
155155
/// This is a version of [`Self::register_static`] which stores rich type information regarding the global.
156-
pub fn register_static_documented<T: TypedScriptReturn + 'static>(
156+
pub fn register_static_documented<T: TypedScriptReturn +'static>(
157157
&mut self,
158158
name: Cow<'static, str>,
159159
documentation: Cow<'static, str>,
@@ -164,7 +164,7 @@ impl ScriptGlobalsRegistry {
164164
maker: None,
165165
documentation: Some(documentation),
166166
type_id: TypeId::of::<T>(),
167-
type_information: Some(T::through_type_info()),
167+
type_information: T::through_type_info(),
168168
},
169169
);
170170
}
@@ -175,7 +175,7 @@ impl ScriptGlobalsRegistry {
175175
pub fn register_static_documented_dynamic(
176176
&mut self,
177177
type_id: TypeId,
178-
type_information: Option<ThroughTypeInfo>,
178+
type_information: ThroughTypeInfo,
179179
name: Cow<'static, str>,
180180
documentation: Cow<'static, str>,
181181
) {

crates/bevy_mod_scripting_core/src/docgen/info.rs

-4
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,9 @@ mod test {
232232
match &info.arg_info[0].type_info {
233233
Some(ThroughTypeInfo::UntypedWrapper {
234234
through_type,
235-
wrapper_type_id,
236235
wrapper_kind,
237236
}) => {
238237
assert_eq!(through_type.type_id(), TypeId::of::<i32>());
239-
assert_eq!(*wrapper_type_id, TypeId::of::<Ref<'static, i32>>());
240238
assert_eq!(*wrapper_kind, UntypedWrapperKind::Ref);
241239
}
242240
_ => panic!("Expected UntypedWrapper"),
@@ -245,11 +243,9 @@ mod test {
245243
match &info.arg_info[1].type_info {
246244
Some(ThroughTypeInfo::UntypedWrapper {
247245
through_type,
248-
wrapper_type_id,
249246
wrapper_kind,
250247
}) => {
251248
assert_eq!(through_type.type_id(), TypeId::of::<f32>());
252-
assert_eq!(*wrapper_type_id, TypeId::of::<Mut<'static, f32>>());
253249
assert_eq!(*wrapper_kind, UntypedWrapperKind::Mut);
254250
}
255251
_ => panic!("Expected UntypedWrapper"),

crates/bevy_mod_scripting_core/src/docgen/typed_through.rs

+131-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Defines a set of traits which destruture [`bevy::reflect::TypeInfo`] and implement a light weight wrapper around it, to allow types
22
//! which normally can't implement [`bevy::reflect::Typed`] to be used in a reflection context.
33
4-
use std::{any::TypeId, ffi::OsString, path::PathBuf};
4+
use std::{ffi::OsString, path::PathBuf};
55

66
use bevy::reflect::{TypeInfo, Typed};
77

@@ -16,7 +16,7 @@ use crate::{
1616
script_value::ScriptValue,
1717
ReflectReference,
1818
},
19-
error::InteropError,
19+
error::InteropError, reflection_extensions::TypeInfoExtensions,
2020
};
2121

2222
/// All Through types follow one rule:
@@ -34,8 +34,6 @@ pub enum ThroughTypeInfo {
3434
UntypedWrapper {
3535
/// The type information of the inner type.
3636
through_type: &'static TypeInfo,
37-
/// The type id of the wrapper type.
38-
wrapper_type_id: TypeId,
3937
/// The name of the wrapper type.
4038
wrapper_kind: UntypedWrapperKind,
4139
},
@@ -75,6 +73,66 @@ pub enum TypedWrapperKind {
7573
Tuple(Vec<ThroughTypeInfo>),
7674
}
7775

76+
/// A dynamic version of [`TypedThrough`], which can be used to convert a [`TypeInfo`] into a [`ThroughTypeInfo`].
77+
pub fn into_through_type_info(type_info: &'static TypeInfo) -> ThroughTypeInfo {
78+
79+
let option = (||{
80+
if let Ok(array) = type_info.as_array() {
81+
let len = array.capacity();
82+
let inner = array.item_info()?;
83+
return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Array(
84+
Box::new(into_through_type_info(inner)),
85+
len,
86+
)));
87+
} else if let Ok(hash_map) = type_info.as_map() {
88+
let key_type = hash_map.key_info()?;
89+
let value_type = hash_map.value_info()?;
90+
91+
return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::HashMap(
92+
Box::new(into_through_type_info(key_type)),
93+
Box::new(into_through_type_info(value_type)),
94+
)));
95+
} else if let Ok(list) = type_info.as_list() {
96+
let inner = list.item_info()?;
97+
return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Vec(
98+
Box::new(into_through_type_info(inner)),
99+
)));
100+
} else if type_info.is_option(){
101+
let enum_ = type_info.as_enum().ok()?;
102+
let inner = enum_.variant("Some")?;
103+
let inner = inner.as_tuple_variant().ok()?;
104+
let inner = inner.field_at(0)?;
105+
let inner = inner.type_info()?;
106+
return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Option(
107+
Box::new(into_through_type_info(inner)),
108+
)));
109+
} else if type_info.is_result() {
110+
let enum_ = type_info.as_enum().ok()?;
111+
// let error_variant = enum_.variant("Err")?;
112+
// TODO verify error variant is InteropError
113+
114+
let inner = enum_.variant("Ok")?;
115+
let inner = inner.as_tuple_variant().ok()?;
116+
let inner = inner.field_at(0)?;
117+
let inner = inner.type_info()?;
118+
return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::InteropResult(
119+
Box::new(into_through_type_info(inner)),
120+
)));
121+
} else if let Ok(tuple) = type_info.as_tuple() {
122+
let mut tuple_types = Vec::new();
123+
for i in 0..tuple.field_len() {
124+
let field = tuple.field_at(i)?;
125+
let field_type = field.type_info()?;
126+
tuple_types.push(into_through_type_info(field_type));
127+
}
128+
return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Tuple(tuple_types)));
129+
}
130+
None
131+
})();
132+
133+
option.unwrap_or(ThroughTypeInfo::UntypedWrapper { through_type: type_info, wrapper_kind: UntypedWrapperKind::Val })
134+
}
135+
78136
/// A trait for types that can be converted to a [`ThroughTypeInfo`].
79137
pub trait TypedThrough {
80138
/// Get the [`ThroughTypeInfo`] for the type.
@@ -94,7 +152,6 @@ impl<T: Typed> TypedThrough for Ref<'_, T> {
94152
fn through_type_info() -> ThroughTypeInfo {
95153
ThroughTypeInfo::UntypedWrapper {
96154
through_type: T::type_info(),
97-
wrapper_type_id: TypeId::of::<Ref<T>>(),
98155
wrapper_kind: UntypedWrapperKind::Ref,
99156
}
100157
}
@@ -104,7 +161,6 @@ impl<T: Typed> TypedThrough for Mut<'_, T> {
104161
fn through_type_info() -> ThroughTypeInfo {
105162
ThroughTypeInfo::UntypedWrapper {
106163
through_type: T::type_info(),
107-
wrapper_type_id: TypeId::of::<Mut<T>>(),
108164
wrapper_kind: UntypedWrapperKind::Mut,
109165
}
110166
}
@@ -114,7 +170,6 @@ impl<T: Typed> TypedThrough for Val<T> {
114170
fn through_type_info() -> ThroughTypeInfo {
115171
ThroughTypeInfo::UntypedWrapper {
116172
through_type: T::type_info(),
117-
wrapper_type_id: TypeId::of::<Val<T>>(),
118173
wrapper_kind: UntypedWrapperKind::Val,
119174
}
120175
}
@@ -207,6 +262,7 @@ macro_rules! impl_through_typed_tuple {
207262

208263
bevy::utils::all_tuples!(impl_through_typed_tuple, 0, 13, T);
209264

265+
210266
#[cfg(test)]
211267
mod test {
212268
use super::*;
@@ -224,29 +280,64 @@ mod test {
224280
}
225281
}
226282

283+
fn assert_dynamic_through_type_is_val_info<T: Typed + TypedThrough> () {
284+
let type_info = T::type_info();
285+
let through_type_info = into_through_type_info(type_info);
286+
287+
match through_type_info {
288+
ThroughTypeInfo::UntypedWrapper{through_type, wrapper_kind} => {
289+
assert_eq!(wrapper_kind, UntypedWrapperKind::Val);
290+
assert_eq!(through_type.type_id(), type_info.type_id());
291+
assert_eq!(through_type.type_path(), type_info.type_path());
292+
}
293+
_ => panic!("Expected ThroughTypeInfo::TypeInfo"),
294+
}
295+
}
296+
227297
#[test]
228298
fn test_typed_through_primitives() {
229299
assert_type_info_is_through::<bool>();
300+
assert_dynamic_through_type_is_val_info::<bool>();
230301
assert_type_info_is_through::<i8>();
302+
assert_dynamic_through_type_is_val_info::<i8>();
231303
assert_type_info_is_through::<i16>();
304+
assert_dynamic_through_type_is_val_info::<i16>();
232305
assert_type_info_is_through::<i32>();
306+
assert_dynamic_through_type_is_val_info::<i32>();
233307
assert_type_info_is_through::<i64>();
308+
assert_dynamic_through_type_is_val_info::<i64>();
234309
assert_type_info_is_through::<i128>();
310+
assert_dynamic_through_type_is_val_info::<i128>();
235311
assert_type_info_is_through::<u8>();
312+
assert_dynamic_through_type_is_val_info::<u8>();
236313
assert_type_info_is_through::<u16>();
314+
assert_dynamic_through_type_is_val_info::<u16>();
237315
assert_type_info_is_through::<u32>();
316+
assert_dynamic_through_type_is_val_info::<u32>();
238317
assert_type_info_is_through::<u64>();
318+
assert_dynamic_through_type_is_val_info::<u64>();
239319
assert_type_info_is_through::<u128>();
320+
assert_dynamic_through_type_is_val_info::<u128>();
240321
assert_type_info_is_through::<f32>();
322+
assert_dynamic_through_type_is_val_info::<f32>();
241323
assert_type_info_is_through::<f64>();
324+
assert_dynamic_through_type_is_val_info::<f64>();
242325
assert_type_info_is_through::<usize>();
326+
assert_dynamic_through_type_is_val_info::<usize>();
243327
assert_type_info_is_through::<isize>();
328+
assert_dynamic_through_type_is_val_info::<isize>();
244329
assert_type_info_is_through::<String>();
330+
assert_dynamic_through_type_is_val_info::<String>();
245331
assert_type_info_is_through::<PathBuf>();
332+
assert_dynamic_through_type_is_val_info::<PathBuf>();
246333
assert_type_info_is_through::<OsString>();
334+
assert_dynamic_through_type_is_val_info::<OsString>();
247335
assert_type_info_is_through::<char>();
336+
assert_dynamic_through_type_is_val_info::<char>();
248337
assert_type_info_is_through::<ReflectReference>();
338+
assert_dynamic_through_type_is_val_info::<ReflectReference>();
249339
assert_type_info_is_through::<&'static str>();
340+
assert_dynamic_through_type_is_val_info::<&'static str>();
250341
}
251342

252343
#[test]
@@ -281,4 +372,37 @@ mod test {
281372
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Tuple(..))
282373
));
283374
}
375+
376+
#[test]
377+
fn test_dynamic_typed_wrapper_outer_variant_matches() {
378+
assert!(matches!(
379+
into_through_type_info(Vec::<i32>::type_info()),
380+
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Vec(..))
381+
));
382+
383+
assert!(matches!(
384+
into_through_type_info(std::collections::HashMap::<i32, f32>::type_info()),
385+
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::HashMap(..))
386+
));
387+
388+
assert!(matches!(
389+
into_through_type_info(Result::<i32, InteropError>::type_info()),
390+
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::InteropResult(..))
391+
));
392+
393+
assert!(matches!(
394+
into_through_type_info(<[i32; 3]>::type_info()),
395+
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Array(..))
396+
));
397+
398+
assert!(matches!(
399+
into_through_type_info(Option::<i32>::type_info()),
400+
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Option(..))
401+
));
402+
403+
assert!(matches!(
404+
into_through_type_info(<(i32, f32)>::type_info()),
405+
ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Tuple(..))
406+
));
407+
}
284408
}

crates/bevy_mod_scripting_core/src/reflection_extensions.rs

+6
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ impl<T: PartialReflect + ?Sized> PartialReflectExt for T {
441441

442442
/// Extension trait for TypeInfos providing additional functionality for working with type information.
443443
pub trait TypeInfoExtensions {
444+
/// Returns true if the type is a result.
445+
fn is_result(&self) -> bool;
444446
/// Returns the inner type of the map if the type is a map, otherwise
445447
fn map_inner_types(&self) -> Option<(TypeId, TypeId)>;
446448
/// Returns the inner type of the list if the type is a list, otherwise None.
@@ -460,6 +462,10 @@ impl TypeInfoExtensions for TypeInfo {
460462
self.is_type(Some("core"), "Option")
461463
}
462464

465+
fn is_result(&self) -> bool {
466+
self.is_type(Some("core"), "Result")
467+
}
468+
463469
fn is_list(&self) -> bool {
464470
matches!(self, TypeInfo::List(_))
465471
}

0 commit comments

Comments
 (0)