diff --git a/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs b/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs index 5f5d46f45..ec3e2655e 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, sync::Arc}; use bevy::{app::Plugin, ecs::reflect::AppTypeRegistry}; use bevy_mod_scripting_derive::script_globals; -use crate::{bindings::{function::from::{Union, Val}, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration, WorldGuard}, error::InteropError}; +use crate::{bindings::{function::from::{Union, Val}, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration, WorldGuard}, docgen::into_through_type_info, error::InteropError}; use super::AppScriptGlobalsRegistry; @@ -38,9 +38,10 @@ fn register_static_core_globals(world: &mut bevy::ecs::world::World) { if let Some(global_name) = registration.type_info().type_path_table().ident() { let documentation = "A reference to the type, allowing you to call static methods."; + let type_info = registration.type_info(); global_registry.register_static_documented_dynamic( registration.type_id(), - None, + into_through_type_info(type_info), global_name.into(), documentation.into(), ); diff --git a/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs b/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs index fedb8197b..649620903 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs @@ -5,8 +5,8 @@ use super::{ script_value::ScriptValue, WorldGuard, }; -use crate::{docgen::typed_through::ThroughTypeInfo, error::InteropError}; -use bevy::{ecs::system::Resource, utils::hashbrown::HashMap}; +use crate::{docgen::{into_through_type_info, typed_through::ThroughTypeInfo}, error::InteropError}; +use bevy::{ecs::system::Resource, reflect::Typed, utils::hashbrown::HashMap}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{any::TypeId, borrow::Cow, sync::Arc}; @@ -45,7 +45,7 @@ pub struct ScriptGlobal { /// The type ID of the global variable. pub type_id: TypeId, /// Rich type information the global variable. - pub type_information: Option, + pub type_information: ThroughTypeInfo, } /// A registry of global variables that can be exposed to scripts. @@ -96,7 +96,7 @@ impl ScriptGlobalsRegistry { /// Inserts a global into the registry, returns the previous value if it existed pub fn register< - T: ScriptReturn + 'static, + T: ScriptReturn + 'static + Typed, F: Fn(WorldGuard) -> Result + 'static + Send + Sync, >( &mut self, @@ -109,7 +109,7 @@ impl ScriptGlobalsRegistry { maker: Some(Self::type_erase_maker(maker)), documentation: None, type_id: TypeId::of::(), - type_information: None, + type_information: into_through_type_info(T::type_info()), }, ) } @@ -132,20 +132,20 @@ impl ScriptGlobalsRegistry { maker: Some(Self::type_erase_maker(maker)), documentation: Some(documentation.into()), type_id: TypeId::of::(), - type_information: Some(T::through_type_info()), + type_information: T::through_type_info(), }, ) } /// Registers a static global into the registry. - pub fn register_static(&mut self, name: Cow<'static, str>) { + pub fn register_static(&mut self, name: Cow<'static, str>) { self.globals.insert( name, ScriptGlobal { maker: None, documentation: None, type_id: TypeId::of::(), - type_information: None, + type_information: into_through_type_info(T::type_info()), }, ); } @@ -153,7 +153,7 @@ impl ScriptGlobalsRegistry { /// Registers a static global into the registry. /// /// This is a version of [`Self::register_static`] which stores rich type information regarding the global. - pub fn register_static_documented( + pub fn register_static_documented( &mut self, name: Cow<'static, str>, documentation: Cow<'static, str>, @@ -164,7 +164,7 @@ impl ScriptGlobalsRegistry { maker: None, documentation: Some(documentation), type_id: TypeId::of::(), - type_information: Some(T::through_type_info()), + type_information: T::through_type_info(), }, ); } @@ -175,7 +175,7 @@ impl ScriptGlobalsRegistry { pub fn register_static_documented_dynamic( &mut self, type_id: TypeId, - type_information: Option, + type_information: ThroughTypeInfo, name: Cow<'static, str>, documentation: Cow<'static, str>, ) { diff --git a/crates/bevy_mod_scripting_core/src/docgen/info.rs b/crates/bevy_mod_scripting_core/src/docgen/info.rs index e0b951dc1..46dcc7e1a 100644 --- a/crates/bevy_mod_scripting_core/src/docgen/info.rs +++ b/crates/bevy_mod_scripting_core/src/docgen/info.rs @@ -232,11 +232,9 @@ mod test { match &info.arg_info[0].type_info { Some(ThroughTypeInfo::UntypedWrapper { through_type, - wrapper_type_id, wrapper_kind, }) => { assert_eq!(through_type.type_id(), TypeId::of::()); - assert_eq!(*wrapper_type_id, TypeId::of::>()); assert_eq!(*wrapper_kind, UntypedWrapperKind::Ref); } _ => panic!("Expected UntypedWrapper"), @@ -245,11 +243,9 @@ mod test { match &info.arg_info[1].type_info { Some(ThroughTypeInfo::UntypedWrapper { through_type, - wrapper_type_id, wrapper_kind, }) => { assert_eq!(through_type.type_id(), TypeId::of::()); - assert_eq!(*wrapper_type_id, TypeId::of::>()); assert_eq!(*wrapper_kind, UntypedWrapperKind::Mut); } _ => panic!("Expected UntypedWrapper"), diff --git a/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs b/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs index 2ce558f7f..d2f5d33e9 100644 --- a/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs +++ b/crates/bevy_mod_scripting_core/src/docgen/typed_through.rs @@ -1,7 +1,7 @@ //! Defines a set of traits which destruture [`bevy::reflect::TypeInfo`] and implement a light weight wrapper around it, to allow types //! which normally can't implement [`bevy::reflect::Typed`] to be used in a reflection context. -use std::{any::TypeId, ffi::OsString, path::PathBuf}; +use std::{ffi::OsString, path::PathBuf}; use bevy::reflect::{TypeInfo, Typed}; @@ -16,7 +16,7 @@ use crate::{ script_value::ScriptValue, ReflectReference, }, - error::InteropError, + error::InteropError, reflection_extensions::TypeInfoExtensions, }; /// All Through types follow one rule: @@ -34,8 +34,6 @@ pub enum ThroughTypeInfo { UntypedWrapper { /// The type information of the inner type. through_type: &'static TypeInfo, - /// The type id of the wrapper type. - wrapper_type_id: TypeId, /// The name of the wrapper type. wrapper_kind: UntypedWrapperKind, }, @@ -75,6 +73,66 @@ pub enum TypedWrapperKind { Tuple(Vec), } +/// A dynamic version of [`TypedThrough`], which can be used to convert a [`TypeInfo`] into a [`ThroughTypeInfo`]. +pub fn into_through_type_info(type_info: &'static TypeInfo) -> ThroughTypeInfo { + + let option = (||{ + if let Ok(array) = type_info.as_array() { + let len = array.capacity(); + let inner = array.item_info()?; + return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Array( + Box::new(into_through_type_info(inner)), + len, + ))); + } else if let Ok(hash_map) = type_info.as_map() { + let key_type = hash_map.key_info()?; + let value_type = hash_map.value_info()?; + + return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::HashMap( + Box::new(into_through_type_info(key_type)), + Box::new(into_through_type_info(value_type)), + ))); + } else if let Ok(list) = type_info.as_list() { + let inner = list.item_info()?; + return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Vec( + Box::new(into_through_type_info(inner)), + ))); + } else if type_info.is_option(){ + let enum_ = type_info.as_enum().ok()?; + let inner = enum_.variant("Some")?; + let inner = inner.as_tuple_variant().ok()?; + let inner = inner.field_at(0)?; + let inner = inner.type_info()?; + return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Option( + Box::new(into_through_type_info(inner)), + ))); + } else if type_info.is_result() { + let enum_ = type_info.as_enum().ok()?; + // let error_variant = enum_.variant("Err")?; + // TODO verify error variant is InteropError + + let inner = enum_.variant("Ok")?; + let inner = inner.as_tuple_variant().ok()?; + let inner = inner.field_at(0)?; + let inner = inner.type_info()?; + return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::InteropResult( + Box::new(into_through_type_info(inner)), + ))); + } else if let Ok(tuple) = type_info.as_tuple() { + let mut tuple_types = Vec::new(); + for i in 0..tuple.field_len() { + let field = tuple.field_at(i)?; + let field_type = field.type_info()?; + tuple_types.push(into_through_type_info(field_type)); + } + return Some(ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Tuple(tuple_types))); + } + None + })(); + + option.unwrap_or(ThroughTypeInfo::UntypedWrapper { through_type: type_info, wrapper_kind: UntypedWrapperKind::Val }) +} + /// A trait for types that can be converted to a [`ThroughTypeInfo`]. pub trait TypedThrough { /// Get the [`ThroughTypeInfo`] for the type. @@ -94,7 +152,6 @@ impl TypedThrough for Ref<'_, T> { fn through_type_info() -> ThroughTypeInfo { ThroughTypeInfo::UntypedWrapper { through_type: T::type_info(), - wrapper_type_id: TypeId::of::>(), wrapper_kind: UntypedWrapperKind::Ref, } } @@ -104,7 +161,6 @@ impl TypedThrough for Mut<'_, T> { fn through_type_info() -> ThroughTypeInfo { ThroughTypeInfo::UntypedWrapper { through_type: T::type_info(), - wrapper_type_id: TypeId::of::>(), wrapper_kind: UntypedWrapperKind::Mut, } } @@ -114,7 +170,6 @@ impl TypedThrough for Val { fn through_type_info() -> ThroughTypeInfo { ThroughTypeInfo::UntypedWrapper { through_type: T::type_info(), - wrapper_type_id: TypeId::of::>(), wrapper_kind: UntypedWrapperKind::Val, } } @@ -207,6 +262,7 @@ macro_rules! impl_through_typed_tuple { bevy::utils::all_tuples!(impl_through_typed_tuple, 0, 13, T); + #[cfg(test)] mod test { use super::*; @@ -224,29 +280,64 @@ mod test { } } + fn assert_dynamic_through_type_is_val_info () { + let type_info = T::type_info(); + let through_type_info = into_through_type_info(type_info); + + match through_type_info { + ThroughTypeInfo::UntypedWrapper{through_type, wrapper_kind} => { + assert_eq!(wrapper_kind, UntypedWrapperKind::Val); + assert_eq!(through_type.type_id(), type_info.type_id()); + assert_eq!(through_type.type_path(), type_info.type_path()); + } + _ => panic!("Expected ThroughTypeInfo::TypeInfo"), + } + } + #[test] fn test_typed_through_primitives() { assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::(); + assert_dynamic_through_type_is_val_info::(); assert_type_info_is_through::<&'static str>(); + assert_dynamic_through_type_is_val_info::<&'static str>(); } #[test] @@ -281,4 +372,37 @@ mod test { ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Tuple(..)) )); } + + #[test] + fn test_dynamic_typed_wrapper_outer_variant_matches() { + assert!(matches!( + into_through_type_info(Vec::::type_info()), + ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Vec(..)) + )); + + assert!(matches!( + into_through_type_info(std::collections::HashMap::::type_info()), + ThroughTypeInfo::TypedWrapper(TypedWrapperKind::HashMap(..)) + )); + + assert!(matches!( + into_through_type_info(Result::::type_info()), + ThroughTypeInfo::TypedWrapper(TypedWrapperKind::InteropResult(..)) + )); + + assert!(matches!( + into_through_type_info(<[i32; 3]>::type_info()), + ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Array(..)) + )); + + assert!(matches!( + into_through_type_info(Option::::type_info()), + ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Option(..)) + )); + + assert!(matches!( + into_through_type_info(<(i32, f32)>::type_info()), + ThroughTypeInfo::TypedWrapper(TypedWrapperKind::Tuple(..)) + )); + } } diff --git a/crates/bevy_mod_scripting_core/src/reflection_extensions.rs b/crates/bevy_mod_scripting_core/src/reflection_extensions.rs index f9e733945..e923c33b3 100644 --- a/crates/bevy_mod_scripting_core/src/reflection_extensions.rs +++ b/crates/bevy_mod_scripting_core/src/reflection_extensions.rs @@ -441,6 +441,8 @@ impl PartialReflectExt for T { /// Extension trait for TypeInfos providing additional functionality for working with type information. pub trait TypeInfoExtensions { + /// Returns true if the type is a result. + fn is_result(&self) -> bool; /// Returns the inner type of the map if the type is a map, otherwise fn map_inner_types(&self) -> Option<(TypeId, TypeId)>; /// Returns the inner type of the list if the type is a list, otherwise None. @@ -460,6 +462,10 @@ impl TypeInfoExtensions for TypeInfo { self.is_type(Some("core"), "Option") } + fn is_result(&self) -> bool { + self.is_type(Some("core"), "Result") + } + fn is_list(&self) -> bool { matches!(self, TypeInfo::List(_)) } diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs index 575ac7cd1..9186a48d9 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs @@ -1,23 +1,38 @@ //! Defines a visitor for function arguments of the `LAD` format. -use ladfile::ArgumentVisitor; +use ladfile::{ArgumentVisitor, LadTypeId}; use crate::markdown::MarkdownBuilder; pub(crate) struct MarkdownArgumentVisitor<'a> { ladfile: &'a ladfile::LadFile, buffer: MarkdownBuilder, + linkifier: Box Option + 'static>, } impl<'a> MarkdownArgumentVisitor<'a> { + /// Create a new instance of the visitor pub fn new(ladfile: &'a ladfile::LadFile) -> Self { let mut builder = MarkdownBuilder::new(); builder.tight_inline().set_escape_mode(false); Self { ladfile, buffer: builder, + linkifier: Box::new(|_, _| None), } } + /// Create a new instance of the visitor with a custom linkifier function + pub fn new_with_linkifier< + F: Fn(LadTypeId, &'a ladfile::LadFile) -> Option + 'static, + >( + ladfile: &'a ladfile::LadFile, + linkifier: F, + ) -> Self { + let mut without = Self::new(ladfile); + without.linkifier = Box::new(linkifier); + without + } + pub fn build(mut self) -> String { self.buffer.build() } @@ -26,8 +41,11 @@ impl<'a> MarkdownArgumentVisitor<'a> { impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { fn visit_lad_type_id(&mut self, type_id: &ladfile::LadTypeId) { // Write identifier - self.buffer.text(self.ladfile.get_type_identifier(type_id)); - if let Some(generics) = self.ladfile.get_type_generics(type_id) { + let generics = self.ladfile.get_type_generics(type_id); + + let type_identifier = self.ladfile.get_type_identifier(type_id); + if let Some(generics) = generics { + self.buffer.text(type_identifier); self.buffer.text('<'); for (i, generic) in generics.iter().enumerate() { self.visit_lad_type_id(&generic.type_id); @@ -36,24 +54,33 @@ impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { } } self.buffer.text('>'); + } else { + // link the type + let link_value = (self.linkifier)(type_id.clone(), self.ladfile); + let link_display = type_identifier; + if let Some(link_value) = link_value { + self.buffer.link(link_display, link_value); + } else { + self.buffer.text(link_display); + } } } - fn walk_option(&mut self, inner: &ladfile::LadArgumentKind) { + fn walk_option(&mut self, inner: &ladfile::LadTypeKind) { // Write Optional self.buffer.text("Optional<"); self.visit(inner); self.buffer.text(">"); } - fn walk_vec(&mut self, inner: &ladfile::LadArgumentKind) { + fn walk_vec(&mut self, inner: &ladfile::LadTypeKind) { // Write Vec self.buffer.text("Vec<"); self.visit(inner); self.buffer.text(">"); } - fn walk_hash_map(&mut self, key: &ladfile::LadArgumentKind, value: &ladfile::LadArgumentKind) { + fn walk_hash_map(&mut self, key: &ladfile::LadTypeKind, value: &ladfile::LadTypeKind) { // Write HashMap self.buffer.text("HashMap<"); self.visit(key); @@ -62,7 +89,7 @@ impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { self.buffer.text(">"); } - fn walk_tuple(&mut self, inner: &[ladfile::LadArgumentKind]) { + fn walk_tuple(&mut self, inner: &[ladfile::LadTypeKind]) { // Write (inner1, inner2, ...) self.buffer.text("("); for (idx, arg) in inner.iter().enumerate() { @@ -74,7 +101,7 @@ impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { self.buffer.text(")"); } - fn walk_union(&mut self, inner: &[ladfile::LadArgumentKind]) { + fn walk_union(&mut self, inner: &[ladfile::LadTypeKind]) { // Write `T1 | T2` for (idx, arg) in inner.iter().enumerate() { self.visit(arg); @@ -84,7 +111,7 @@ impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { } } - fn walk_array(&mut self, inner: &ladfile::LadArgumentKind, size: usize) { + fn walk_array(&mut self, inner: &ladfile::LadTypeKind, size: usize) { // Write [inner; size] self.buffer.text("["); self.visit(inner); @@ -96,7 +123,7 @@ impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { #[cfg(test)] mod test { - use ladfile::LadArgumentKind; + use ladfile::LadTypeKind; use super::*; @@ -106,6 +133,20 @@ mod test { ladfile::parse_lad_file(ladfile).unwrap() } + #[test] + fn test_linkifier_visitor_creates_links() { + let ladfile = setup_ladfile(); + + let mut visitor = + MarkdownArgumentVisitor::new_with_linkifier(&ladfile, |type_id, ladfile| { + Some(format!("root/{}", ladfile.get_type_identifier(&type_id))) + }); + + let first_type_id = ladfile.types.first().unwrap().0; + visitor.visit_lad_type_id(first_type_id); + assert_eq!(visitor.buffer.build(), "[EnumType](root/EnumType)"); + } + #[test] fn test_visit_type_id() { let ladfile = setup_ladfile(); @@ -130,7 +171,7 @@ mod test { let first_type_id = ladfile.types.first().unwrap().0; let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Ref(first_type_id.clone())); + visitor.visit(&LadTypeKind::Ref(first_type_id.clone())); assert_eq!(visitor.buffer.build(), "EnumType"); } @@ -141,7 +182,7 @@ mod test { let first_type_id = ladfile.types.first().unwrap().0; let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Mut(first_type_id.clone())); + visitor.visit(&LadTypeKind::Mut(first_type_id.clone())); assert_eq!(visitor.buffer.build(), "EnumType"); } @@ -152,7 +193,7 @@ mod test { let first_type_id = ladfile.types.first().unwrap().0; let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Val(first_type_id.clone())); + visitor.visit(&LadTypeKind::Val(first_type_id.clone())); assert_eq!(visitor.buffer.build(), "EnumType"); } @@ -162,9 +203,9 @@ mod test { let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Option(Box::new( - LadArgumentKind::Primitive(ladfile::LadBMSPrimitiveKind::Bool), - ))); + visitor.visit(&LadTypeKind::Option(Box::new(LadTypeKind::Primitive( + ladfile::LadBMSPrimitiveKind::Bool, + )))); assert_eq!(visitor.buffer.build(), "Optional"); } @@ -174,7 +215,7 @@ mod test { let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Vec(Box::new(LadArgumentKind::Primitive( + visitor.visit(&LadTypeKind::Vec(Box::new(LadTypeKind::Primitive( ladfile::LadBMSPrimitiveKind::Bool, )))); assert_eq!(visitor.buffer.build(), "Vec"); @@ -186,26 +227,46 @@ mod test { let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::HashMap( - Box::new(LadArgumentKind::Primitive( - ladfile::LadBMSPrimitiveKind::Bool, - )), - Box::new(LadArgumentKind::Primitive( - ladfile::LadBMSPrimitiveKind::String, - )), + visitor.visit(&LadTypeKind::HashMap( + Box::new(LadTypeKind::Primitive(ladfile::LadBMSPrimitiveKind::Bool)), + Box::new(LadTypeKind::Primitive(ladfile::LadBMSPrimitiveKind::String)), )); + assert_eq!(visitor.buffer.build(), "HashMap"); } + #[test] + fn test_visit_nested_hash_map() { + let ladfile = setup_ladfile(); + + let mut visitor = MarkdownArgumentVisitor::new(&ladfile); + let first_type_id = ladfile.types.first().unwrap().0; + + visitor.visit(&LadTypeKind::HashMap( + Box::new(LadTypeKind::Primitive(ladfile::LadBMSPrimitiveKind::Bool)), + Box::new(LadTypeKind::Union(vec![ + LadTypeKind::Val(first_type_id.clone()), + LadTypeKind::Union(vec![ + LadTypeKind::Val(first_type_id.clone()), + LadTypeKind::Val(first_type_id.clone()), + ]), + ])), + )); + assert_eq!( + visitor.buffer.build(), + "HashMap" + ); + } + #[test] fn test_visit_tuple() { let ladfile = setup_ladfile(); let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Tuple(vec![ - LadArgumentKind::Primitive(ladfile::LadBMSPrimitiveKind::Bool), - LadArgumentKind::Primitive(ladfile::LadBMSPrimitiveKind::String), + visitor.visit(&LadTypeKind::Tuple(vec![ + LadTypeKind::Primitive(ladfile::LadBMSPrimitiveKind::Bool), + LadTypeKind::Primitive(ladfile::LadBMSPrimitiveKind::String), ])); assert_eq!(visitor.buffer.build(), "(bool, String)"); } @@ -216,10 +277,8 @@ mod test { let mut visitor = MarkdownArgumentVisitor::new(&ladfile); - visitor.visit(&LadArgumentKind::Array( - Box::new(LadArgumentKind::Primitive( - ladfile::LadBMSPrimitiveKind::Bool, - )), + visitor.visit(&LadTypeKind::Array( + Box::new(LadTypeKind::Primitive(ladfile::LadBMSPrimitiveKind::Bool)), 5, )); assert_eq!(visitor.buffer.build(), "[bool; 5]"); @@ -233,7 +292,7 @@ mod test { let first_type_id = ladfile.types.first().unwrap().0; - visitor.visit(&LadArgumentKind::Unknown(first_type_id.clone())); + visitor.visit(&LadTypeKind::Unknown(first_type_id.clone())); assert_eq!(visitor.buffer.build(), "EnumType"); } } diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs index 4f0847af4..68dba62c7 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs @@ -94,9 +94,7 @@ pub enum Markdown { headers: Vec, rows: Vec>, }, - Raw { - text: String, - }, + Raw(String), } #[allow(dead_code)] @@ -174,6 +172,7 @@ impl IntoMarkdown for Markdown { } let escaped = if *code { + // this might be a bug in the markdown renderer but we need to escape those for tables text.clone() } else { escape_markdown(text, builder.escape) @@ -278,7 +277,7 @@ impl IntoMarkdown for Markdown { header_line, separator_line, rows_lines )); } - Markdown::Raw { text } => { + Markdown::Raw(text) => { builder.append(text); } } @@ -649,7 +648,7 @@ mod tests { .row(vec!["Row 1 Col 1", "Row 1 Col 2"]) .row(markdown_vec![ "Row 2 Col 1", - Markdown::new_paragraph("some_code").code() + Markdown::new_paragraph("HashMap").code() ]); }) .build(); @@ -679,7 +678,7 @@ mod tests { | Header 1 | Header 2 | | --- | --- | | Row 1 Col 1 | Row 1 Col 2 | - | Row 2 Col 1 | `some_code` | + | Row 2 Col 1 | `HashMap` | "#; let trimmed_indentation_expected = expected diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs index f2484c9b3..7d83ae894 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs @@ -16,6 +16,14 @@ fn print_type(ladfile: &LadFile, type_: &LadTypeId) -> String { visitor.build() } +fn build_escaped_visitor(arg_visitor: MarkdownArgumentVisitor<'_>) -> String { + arg_visitor + .build() + .replace("<", "\\<") + .replace(">", "\\>") + .replace("|", "\\|") +} + /// Sections which convert to single markdown files pub(crate) enum Section<'a> { Summary { @@ -205,7 +213,12 @@ impl<'a> Section<'a> { } Section::InstancesSummary { ladfile } => { let instances = ladfile.globals.iter().collect::>(); - vec![SectionItem::InstancesSummary { instances }] + let types_directory = linkify_filename(Section::TypeSummary { ladfile }.title()); + vec![SectionItem::InstancesSummary { + instances, + ladfile, + types_directory, + }] } Section::TypeSummary { ladfile } => { let types = ladfile.types.keys().collect::>(); @@ -297,7 +310,9 @@ pub enum SectionItem<'a> { ladfile: &'a ladfile::LadFile, }, InstancesSummary { + ladfile: &'a ladfile::LadFile, instances: Vec<(&'a Cow<'static, str>, &'a LadInstance)>, + types_directory: String, }, } @@ -347,13 +362,13 @@ impl IntoMarkdown for SectionItem<'_> { SectionItem::Description { lad_type: description, } => { - builder.heading(2, "Description").quote(Markdown::Raw { - text: description + builder.heading(2, "Description").quote(Markdown::Raw( + description .documentation .as_deref() .unwrap_or(NO_DOCS_STRING) .to_owned(), - }); + )); } SectionItem::FunctionsSummary { functions, @@ -432,31 +447,68 @@ impl IntoMarkdown for SectionItem<'_> { } }); } - SectionItem::InstancesSummary { instances } => { - builder.heading(2, "Globals"); - + SectionItem::InstancesSummary { + instances, + ladfile, + types_directory, + } => { + builder.heading(2, "Global Values"); + builder.text("Global values that are accessible anywhere inside scripts. You should avoid naming conflicts with these and trying to overwrite or edit them."); // make a table of instances as a quick reference, make them link to instance details sub-sections + + // first build a non-static instance table + let instances = instances + .iter() + .map(|(k, v)| { + let name = k.to_string(); + let types_directory = types_directory.clone(); + let mut arg_visitor = MarkdownArgumentVisitor::new_with_linkifier( + ladfile, + move |lad_type_id, ladfile| { + let printed_type = + linkify_filename(print_type(ladfile, &lad_type_id)); + Some(format!("./{types_directory}/{printed_type}.md")) + }, + ); + arg_visitor.visit(&v.type_kind); + let escaped = build_escaped_visitor(arg_visitor); + (v.is_static, name, escaped) + }) + .collect::>(); + + builder.heading(3, "Instances"); + builder.text("Instances containing actual accessible values."); builder.table(|builder| { builder.headers(vec!["Instance", "Type"]); - for (key, instance) in instances.iter() { - let first_col = key.to_string(); + for (_, name, instance) in instances.iter().filter(|(a, _, _)| !*a) { + builder.row(markdown_vec![ + Markdown::new_paragraph(name).code(), + Markdown::Raw(instance.clone()) + ]); + } + }); + builder.heading(3, "Static Instances"); + builder.text("Static type references, existing for the purpose of typed static function calls."); + builder.table(|builder| { + builder.headers(vec!["Instance", "Type"]); + for (_, name, instance) in instances.iter().filter(|(a, _, _)| *a) { builder.row(markdown_vec![ - Markdown::new_paragraph(first_col).code(), - Markdown::new_paragraph(instance.type_id.to_string()) + Markdown::new_paragraph(name).code(), + Markdown::Raw(instance.clone()) ]); } }); } SectionItem::FunctionDetails { function, ladfile } => { // we don't escape this, this is already markdown - builder.quote(Markdown::Raw { - text: function + builder.quote(Markdown::Raw( + function .documentation .as_deref() .unwrap_or(NO_DOCS_STRING) .to_owned(), - }); + )); builder.heading(4, "Arguments"); builder.list( @@ -498,12 +550,11 @@ fn lad_argument_to_list_elem( Markdown::new_paragraph(":"), Markdown::new_paragraph(markdown).code(), Markdown::new_paragraph("-"), - Markdown::Raw { - text: arg - .documentation + Markdown::Raw( + arg.documentation .as_deref() .unwrap_or(NO_DOCS_STRING) .to_owned() - } + ) ] } diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/globals.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/globals.md index 1a37b6fe8..d27fb52df 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/globals.md +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/globals.md @@ -1,8 +1,22 @@ # Globals -## Globals +## Global Values + +Global values that are accessible anywhere inside scripts\. You should avoid naming conflicts with these and trying to overwrite or edit them\. + +### Instances + +Instances containing actual accessible values\. + +| Instance | Type | +| --- | --- | +| `my_non_static_instance` | Vec\<[UnitType](./types/unittype.md)\> | +| `map` | HashMap\<[String](./types/string.md), [String](./types/string.md) \| [String](./types/string.md)\> | + +### Static Instances + +Static type references, existing for the purpose of typed static function calls\. | Instance | Type | | --- | --- | -| `my_static_instance` | ladfile\_builder::test::StructType | -| `my_non_static_instance` | ladfile\_builder::test::UnitType | \ No newline at end of file +| `my_static_instance` | StructType\<[usize](./types/usize.md)\> | \ No newline at end of file diff --git a/crates/ladfile/src/lib.rs b/crates/ladfile/src/lib.rs index a702a7f94..55ec97de2 100644 --- a/crates/ladfile/src/lib.rs +++ b/crates/ladfile/src/lib.rs @@ -93,8 +93,8 @@ impl Default for LadFile { /// A LAD global instance #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct LadInstance { - /// The type of the instance - pub type_id: LadTypeId, + /// The kind of the instance + pub type_kind: LadTypeKind, /// whether the instance is static or not /// @@ -154,7 +154,7 @@ pub enum LadFunctionNamespace { /// An argument definition used in a LAD file. pub struct LadArgument { /// The kind and type of argument - pub kind: LadArgumentKind, + pub kind: LadTypeKind, /// The provided documentation for this argument. Normally derived from the function docstring. #[serde(skip_serializing_if = "Option::is_none", default)] @@ -166,9 +166,17 @@ pub struct LadArgument { } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -/// The kind of an argument in a LAD file. +/// The kind of type in a LAD file. +/// There is a distinction between the "core" identity of a type +/// and how it's used in various contexts. +/// +/// for example: +/// - `Vec` is a list of `T`'s +/// - `T` IS T +/// +/// In generating documents, it's convenient to distinguish a few core "containers" to provide useful information. #[serde(rename_all = "camelCase")] -pub enum LadArgumentKind { +pub enum LadTypeKind { /// a `Ref` wrapped argument Ref(LadTypeId), /// a `Mut` wrapped argument @@ -176,21 +184,21 @@ pub enum LadArgumentKind { /// a `Val` wrapped argument Val(LadTypeId), /// an `Option` wrapped argument - Option(Box), + Option(Box), /// a `Vec` - Vec(Box), + Vec(Box), /// a `HashMap` - HashMap(Box, Box), + HashMap(Box, Box), /// A `InteropResult` - InteropResult(Box), + InteropResult(Box), /// A tuple of arguments - Tuple(Vec), + Tuple(Vec), /// An array - Array(Box, usize), + Array(Box, usize), /// A primitive type, implementing `IntoScript` and `FromScript` natively in BMS. Primitive(LadBMSPrimitiveKind), /// A union of two or more types - Union(Vec), + Union(Vec), /// An arbitrary type which is either unsupported, doesn't contain type information, or is generally unknown. /// /// This will be the variant used for external primitives as well. @@ -226,35 +234,35 @@ pub trait ArgumentVisitor { } /// traverse an `Option` wrapped argument, by default calls `visit` on the inner argument - fn walk_option(&mut self, inner: &LadArgumentKind) { + fn walk_option(&mut self, inner: &LadTypeKind) { self.visit(inner); } /// traverse a `Vec` wrapped argument, by default calls `visit` on the inner argument - fn walk_vec(&mut self, inner: &LadArgumentKind) { + fn walk_vec(&mut self, inner: &LadTypeKind) { self.visit(inner); } /// traverse a `HashMap` wrapped argument, by default calls `visit` on the key and value - fn walk_hash_map(&mut self, key: &LadArgumentKind, value: &LadArgumentKind) { + fn walk_hash_map(&mut self, key: &LadTypeKind, value: &LadTypeKind) { self.visit(key); self.visit(value); } /// traverse an `InteropResult` wrapped argument, by default calls `visit` on the inner argument - fn walk_interop_result(&mut self, inner: &LadArgumentKind) { + fn walk_interop_result(&mut self, inner: &LadTypeKind) { self.visit(inner); } /// traverse a tuple of arguments, by default calls `visit` on each argument - fn walk_tuple(&mut self, inner: &[LadArgumentKind]) { + fn walk_tuple(&mut self, inner: &[LadTypeKind]) { for arg in inner { self.visit(arg); } } /// traverse an array of arguments, by default calls `visit` on the inner argument - fn walk_array(&mut self, inner: &LadArgumentKind, size: usize) { + fn walk_array(&mut self, inner: &LadTypeKind, size: usize) { self.visit(inner); } @@ -264,7 +272,7 @@ pub trait ArgumentVisitor { } /// traverse a union of arguments, by default calls `visit` on each argument - fn walk_union(&mut self, inner: &[LadArgumentKind]) { + fn walk_union(&mut self, inner: &[LadTypeKind]) { for arg in inner { self.visit(arg); } @@ -280,20 +288,20 @@ pub trait ArgumentVisitor { /// Each walk variant will walk over nested kinds, and visit the leaf types. /// /// If you want to do something with the parent types, you WILL have to override each individual walk method. - fn visit(&mut self, kind: &LadArgumentKind) { + fn visit(&mut self, kind: &LadTypeKind) { match kind { - LadArgumentKind::Ref(type_id) => self.walk_ref(type_id), - LadArgumentKind::Mut(type_id) => self.walk_mut(type_id), - LadArgumentKind::Val(type_id) => self.walk_val(type_id), - LadArgumentKind::Option(inner) => self.walk_option(inner), - LadArgumentKind::Vec(inner) => self.walk_vec(inner), - LadArgumentKind::HashMap(key, value) => self.walk_hash_map(key, value), - LadArgumentKind::InteropResult(inner) => self.walk_interop_result(inner), - LadArgumentKind::Tuple(inner) => self.walk_tuple(inner), - LadArgumentKind::Array(inner, size) => self.walk_array(inner, *size), - LadArgumentKind::Primitive(primitive_kind) => self.walk_primitive(primitive_kind), - LadArgumentKind::Union(inner) => self.walk_union(inner), - LadArgumentKind::Unknown(type_id) => self.walk_unknown(type_id), + LadTypeKind::Ref(type_id) => self.walk_ref(type_id), + LadTypeKind::Mut(type_id) => self.walk_mut(type_id), + LadTypeKind::Val(type_id) => self.walk_val(type_id), + LadTypeKind::Option(inner) => self.walk_option(inner), + LadTypeKind::Vec(inner) => self.walk_vec(inner), + LadTypeKind::HashMap(key, value) => self.walk_hash_map(key, value), + LadTypeKind::InteropResult(inner) => self.walk_interop_result(inner), + LadTypeKind::Tuple(inner) => self.walk_tuple(inner), + LadTypeKind::Array(inner, size) => self.walk_array(inner, *size), + LadTypeKind::Primitive(primitive_kind) => self.walk_primitive(primitive_kind), + LadTypeKind::Union(inner) => self.walk_union(inner), + LadTypeKind::Unknown(type_id) => self.walk_unknown(type_id), } } } diff --git a/crates/ladfile/test_assets/test.lad.json b/crates/ladfile/test_assets/test.lad.json index 27a318c14..2b8686d39 100644 --- a/crates/ladfile/test_assets/test.lad.json +++ b/crates/ladfile/test_assets/test.lad.json @@ -2,11 +2,37 @@ "version": "{{version}}", "globals": { "my_static_instance": { - "type_id": "ladfile_builder::test::StructType", + "type_kind": { + "val": "ladfile_builder::test::StructType" + }, "is_static": true }, "my_non_static_instance": { - "type_id": "ladfile_builder::test::UnitType", + "type_kind": { + "vec": { + "val": "ladfile_builder::test::UnitType" + } + }, + "is_static": false + }, + "map": { + "type_kind": { + "hashMap": [ + { + "primitive": "string" + }, + { + "union": [ + { + "primitive": "string" + }, + { + "primitive": "string" + } + ] + } + ] + }, "is_static": false } }, diff --git a/crates/ladfile_builder/src/lib.rs b/crates/ladfile_builder/src/lib.rs index 6f8c3490a..9f0693fb3 100644 --- a/crates/ladfile_builder/src/lib.rs +++ b/crates/ladfile_builder/src/lib.rs @@ -14,6 +14,7 @@ use bevy_mod_scripting_core::{ docgen::{ info::FunctionInfo, typed_through::{ThroughTypeInfo, TypedWrapperKind, UntypedWrapperKind}, + TypedThrough, }, match_by_type, }; @@ -143,15 +144,20 @@ impl<'t> LadFileBuilder<'t> { /// /// If `is_static` is true, the instance will be treated as a static instance /// and hence not support method call syntax or method calls (i.e. only functions without a self parameter can be called on them). - pub fn add_instance( + pub fn add_instance( &mut self, key: impl Into>, is_static: bool, ) -> &mut Self { - let type_id = self.lad_id_from_type_id(TypeId::of::()); - self.file - .globals - .insert(key.into(), LadInstance { type_id, is_static }); + let type_info = T::through_type_info(); + let type_kind = self.lad_type_kind_from_through_type(&type_info); + self.file.globals.insert( + key.into(), + LadInstance { + type_kind, + is_static, + }, + ); self } @@ -162,12 +168,16 @@ impl<'t> LadFileBuilder<'t> { &mut self, key: impl Into>, is_static: bool, - type_id: TypeId, + through_type: ThroughTypeInfo, ) -> &mut Self { - let type_id = self.lad_id_from_type_id(type_id); - self.file - .globals - .insert(key.into(), LadInstance { type_id, is_static }); + let type_kind = self.lad_type_kind_from_through_type(&through_type); + self.file.globals.insert( + key.into(), + LadInstance { + type_kind, + is_static, + }, + ); self } @@ -239,10 +249,8 @@ impl<'t> LadFileBuilder<'t> { .into_iter() .map(|arg| { let kind = match &arg.type_info { - Some(through_type) => { - self.lad_argument_type_from_through_type(through_type) - } - None => LadArgumentKind::Unknown(self.lad_id_from_type_id(arg.type_id)), + Some(through_type) => self.lad_type_kind_from_through_type(through_type), + None => LadTypeKind::Unknown(self.lad_id_from_type_id(arg.type_id)), }; LadArgument { kind, @@ -260,9 +268,9 @@ impl<'t> LadFileBuilder<'t> { kind: function_info .return_info .type_info - .map(|info| self.lad_argument_type_from_through_type(&info)) + .map(|info| self.lad_type_kind_from_through_type(&info)) .unwrap_or_else(|| { - LadArgumentKind::Unknown( + LadTypeKind::Unknown( self.lad_id_from_type_id(function_info.return_info.type_id), ) }), @@ -550,10 +558,7 @@ impl<'t> LadFileBuilder<'t> { LadFunctionId::new_string_id(format!("{}::{}", namespace_string, function_info.name)) } - fn lad_argument_type_from_through_type( - &mut self, - through_type: &ThroughTypeInfo, - ) -> LadArgumentKind { + fn lad_type_kind_from_through_type(&mut self, through_type: &ThroughTypeInfo) -> LadTypeKind { match through_type { ThroughTypeInfo::UntypedWrapper { through_type, @@ -561,58 +566,56 @@ impl<'t> LadFileBuilder<'t> { .. } => match wrapper_kind { UntypedWrapperKind::Ref => { - LadArgumentKind::Ref(self.lad_id_from_type_id(through_type.type_id())) + LadTypeKind::Ref(self.lad_id_from_type_id(through_type.type_id())) } UntypedWrapperKind::Mut => { - LadArgumentKind::Mut(self.lad_id_from_type_id(through_type.type_id())) + LadTypeKind::Mut(self.lad_id_from_type_id(through_type.type_id())) } UntypedWrapperKind::Val => { - LadArgumentKind::Val(self.lad_id_from_type_id(through_type.type_id())) + LadTypeKind::Val(self.lad_id_from_type_id(through_type.type_id())) } }, ThroughTypeInfo::TypedWrapper(typed_wrapper_kind) => match typed_wrapper_kind { - TypedWrapperKind::Vec(through_type_info) => LadArgumentKind::Vec(Box::new( - self.lad_argument_type_from_through_type(through_type_info), + TypedWrapperKind::Vec(through_type_info) => LadTypeKind::Vec(Box::new( + self.lad_type_kind_from_through_type(through_type_info), )), TypedWrapperKind::HashMap(through_type_info, through_type_info1) => { - LadArgumentKind::HashMap( - Box::new(self.lad_argument_type_from_through_type(through_type_info)), - Box::new(self.lad_argument_type_from_through_type(through_type_info1)), + LadTypeKind::HashMap( + Box::new(self.lad_type_kind_from_through_type(through_type_info)), + Box::new(self.lad_type_kind_from_through_type(through_type_info1)), ) } - TypedWrapperKind::Array(through_type_info, size) => LadArgumentKind::Array( - Box::new(self.lad_argument_type_from_through_type(through_type_info)), + TypedWrapperKind::Array(through_type_info, size) => LadTypeKind::Array( + Box::new(self.lad_type_kind_from_through_type(through_type_info)), *size, ), - TypedWrapperKind::Option(through_type_info) => LadArgumentKind::Option(Box::new( - self.lad_argument_type_from_through_type(through_type_info), + TypedWrapperKind::Option(through_type_info) => LadTypeKind::Option(Box::new( + self.lad_type_kind_from_through_type(through_type_info), )), - TypedWrapperKind::InteropResult(through_type_info) => { - LadArgumentKind::InteropResult(Box::new( - self.lad_argument_type_from_through_type(through_type_info), - )) - } - TypedWrapperKind::Tuple(through_type_infos) => LadArgumentKind::Tuple( + TypedWrapperKind::InteropResult(through_type_info) => LadTypeKind::InteropResult( + Box::new(self.lad_type_kind_from_through_type(through_type_info)), + ), + TypedWrapperKind::Tuple(through_type_infos) => LadTypeKind::Tuple( through_type_infos .iter() .map(|through_type_info| { - self.lad_argument_type_from_through_type(through_type_info) + self.lad_type_kind_from_through_type(through_type_info) }) .collect(), ), - TypedWrapperKind::Union(through_type_infos) => LadArgumentKind::Union( + TypedWrapperKind::Union(through_type_infos) => LadTypeKind::Union( through_type_infos .iter() .map(|through_type_info| { - self.lad_argument_type_from_through_type(through_type_info) + self.lad_type_kind_from_through_type(through_type_info) }) .collect(), ), }, ThroughTypeInfo::TypeInfo(type_info) => { match primitive_from_type_id(type_info.type_id()) { - Some(primitive) => LadArgumentKind::Primitive(primitive), - None => LadArgumentKind::Unknown(self.lad_id_from_type_id(type_info.type_id())), + Some(primitive) => LadTypeKind::Primitive(primitive), + None => LadTypeKind::Unknown(self.lad_id_from_type_id(type_info.type_id())), } } } @@ -622,9 +625,12 @@ impl<'t> LadFileBuilder<'t> { #[cfg(test)] mod test { use bevy_mod_scripting_core::{ - bindings::function::{ - from::Ref, - namespace::{GlobalNamespace, IntoNamespace}, + bindings::{ + function::{ + from::Ref, + namespace::{GlobalNamespace, IntoNamespace}, + }, + Union, Val, }, docgen::info::GetFunctionInfo, }; @@ -632,9 +638,6 @@ mod test { use super::*; - /// Set to true to put output into test_assets. - const BLESS_TEST_FILE: bool = false; - /// normalize line endings etc.. fn normalize_file(file: &mut String) { *file = file.replace("\r\n", "\n"); @@ -830,6 +833,9 @@ mod test { ); } + /// Set to true to put output into test_assets. + const BLESS_TEST_FILE: bool = false; + #[test] fn test_serializes_as_expected() { let mut type_registry = TypeRegistry::default(); @@ -843,10 +849,22 @@ mod test { field2: T, } + impl TypedThrough for StructType { + fn through_type_info() -> ThroughTypeInfo { + ThroughTypeInfo::TypeInfo(Self::type_info()) + } + } + #[derive(Reflect)] /// I am a unit test type struct UnitType; + impl TypedThrough for UnitType { + fn through_type_info() -> ThroughTypeInfo { + ThroughTypeInfo::TypeInfo(Self::type_info()) + } + } + #[derive(Reflect)] /// I am a tuple test type struct TupleStructType(pub usize, #[doc = "hello"] pub String); @@ -908,8 +926,9 @@ mod test { .add_type::() .add_type::() .add_type_info(EnumType::type_info()) - .add_instance::>("my_static_instance", true) - .add_instance::("my_non_static_instance", false) + .add_instance::>>("my_static_instance", true) + .add_instance::>>("my_non_static_instance", false) + .add_instance::>>("map", false) .build(); // normalize the version so we don't have to update it every time @@ -927,7 +946,7 @@ mod test { println!("Blessing test file at {:?}", path_to_test_assets); std::fs::write(path_to_test_assets.join("test.lad.json"), &serialized).unwrap(); - return; + panic!("Blessed test file, please rerun the test"); } let mut expected = ladfile::EXAMPLE_LADFILE.to_string(); diff --git a/crates/ladfile_builder/src/plugin.rs b/crates/ladfile_builder/src/plugin.rs index 4f4ba30e1..1c6f5851d 100644 --- a/crates/ladfile_builder/src/plugin.rs +++ b/crates/ladfile_builder/src/plugin.rs @@ -100,7 +100,8 @@ pub fn generate_lad_file( // find global instances for (key, global) in global_registry.iter() { - builder.add_instance_dynamic(key.to_string(), global.maker.is_none(), global.type_id); + let type_info = global.type_information.clone(); + builder.add_instance_dynamic(key.to_string(), global.maker.is_none(), type_info); } let file = builder.build();