From 262846e702f700ea6a28e1598416eeb79b2dda31 Mon Sep 17 00:00:00 2001 From: radiish Date: Mon, 9 Oct 2023 20:33:03 +0100 Subject: [PATCH] reflect: `TypePath` part 2 (#8768) # Objective - Followup to #7184. - ~Deprecate `TypeUuid` and remove its internal references.~ No longer part of this PR. - Use `TypePath` for the type registry, and (de)serialisation instead of `std::any::type_name`. - Allow accessing type path information behind proxies. ## Solution - Introduce methods on `TypeInfo` and friends for dynamically querying type path. These methods supersede the old `type_name` methods. - Remove `Reflect::type_name` in favor of `DynamicTypePath::type_path` and `TypeInfo::type_path_table`. - Switch all uses of `std::any::type_name` in reflection, non-debugging contexts to use `TypePath`. --- ## Changelog - Added `TypePathTable` for dynamically accessing methods on `TypePath` through `TypeInfo` and the type registry. - Removed `type_name` from all `TypeInfo`-like structs. - Added `type_path` and `type_path_table` methods to all `TypeInfo`-like structs. - Removed `Reflect::type_name` in favor of `DynamicTypePath::reflect_type_path` and `TypeInfo::type_path`. - Changed the signature of all `DynamicTypePath` methods to return strings with a static lifetime. ## Migration Guide - Rely on `TypePath` instead of `std::any::type_name` for all stability guarantees and for use in all reflection contexts, this is used through with one of the following APIs: - `TypePath::type_path` if you have a concrete type and not a value. - `DynamicTypePath::reflect_type_path` if you have an `dyn Reflect` value without a concrete type. - `TypeInfo::type_path` for use through the registry or if you want to work with the represented type of a `DynamicFoo`. - Remove `type_name` from manual `Reflect` implementations. - Use `type_path` and `type_path_table` in place of `type_name` on `TypeInfo`-like structs. - Use `get_with_type_path(_mut)` over `get_with_type_name(_mut)`. ## Note to reviewers I think if anything we were a little overzealous in merging #7184 and we should take that extra care here. In my mind, this is the "point of no return" for `TypePath` and while I think we all agree on the design, we should carefully consider if the finer details and current implementations are actually how we want them moving forward. For example [this incorrect `TypePath` implementation for `String`](https://github.com/soqb/bevy/blob/3fea3c6c0b5719dfbd3d4230f5282ec80d82556a/crates/bevy_reflect/src/impls/std.rs#L90) (note that `String` is in the default Rust prelude) snuck in completely under the radar. --- crates/bevy_app/src/app.rs | 2 +- crates/bevy_ecs/src/reflect/bundle.rs | 14 +- .../bevy_ecs/src/reflect/entity_commands.rs | 26 +-- .../bevy_reflect_derive/src/derive_data.rs | 6 +- .../bevy_reflect_derive/src/from_reflect.rs | 2 +- .../bevy_reflect_derive/src/impls/enums.rs | 15 +- .../bevy_reflect_derive/src/impls/structs.rs | 11 +- .../src/impls/tuple_structs.rs | 11 +- .../bevy_reflect_derive/src/impls/values.rs | 7 +- crates/bevy_reflect/src/array.rs | 45 ++-- crates/bevy_reflect/src/enums/dynamic_enum.rs | 9 +- crates/bevy_reflect/src/enums/enum_trait.rs | 36 ++- crates/bevy_reflect/src/enums/mod.rs | 12 +- crates/bevy_reflect/src/fields.rs | 50 +++-- crates/bevy_reflect/src/from_reflect.rs | 4 +- crates/bevy_reflect/src/impls/smallvec.rs | 20 +- crates/bevy_reflect/src/impls/std.rs | 169 +++++--------- crates/bevy_reflect/src/lib.rs | 211 +++++++++--------- crates/bevy_reflect/src/list.rs | 44 ++-- crates/bevy_reflect/src/map.rs | 63 +++--- crates/bevy_reflect/src/reflect.rs | 50 ++++- crates/bevy_reflect/src/serde/de.rs | 92 ++++---- crates/bevy_reflect/src/serde/mod.rs | 2 +- crates/bevy_reflect/src/serde/ser.rs | 40 ++-- crates/bevy_reflect/src/struct_trait.rs | 40 ++-- crates/bevy_reflect/src/tuple.rs | 128 ++++++----- crates/bevy_reflect/src/tuple_struct.rs | 42 ++-- crates/bevy_reflect/src/type_info.rs | 76 ++++--- crates/bevy_reflect/src/type_path.rs | 73 +++++- crates/bevy_reflect/src/type_registry.rs | 148 +++++------- crates/bevy_reflect/src/utility.rs | 62 +++-- crates/bevy_scene/src/dynamic_scene.rs | 34 ++- crates/bevy_scene/src/scene.rs | 8 +- crates/bevy_scene/src/scene_spawner.rs | 32 ++- crates/bevy_scene/src/serde.rs | 14 +- examples/reflection/generic_reflection.rs | 5 +- 36 files changed, 816 insertions(+), 787 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 9094773e4e678..2b942f94eca67 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -728,7 +728,7 @@ impl App { /// See [`bevy_reflect::TypeRegistry::register_type_data`]. #[cfg(feature = "bevy_reflect")] pub fn register_type_data< - T: bevy_reflect::Reflect + 'static, + T: bevy_reflect::Reflect + bevy_reflect::TypePath, D: bevy_reflect::TypeData + bevy_reflect::FromType, >( &mut self, diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index 8b5f5e20843c8..4c7e9ed1f777b 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -146,7 +146,8 @@ impl FromType for ReflectBundle { .for_each(|field| insert_field::(entity, field, registry)), _ => panic!( "expected bundle `{}` to be named struct or tuple", - std::any::type_name::() + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), ), } }, @@ -163,7 +164,8 @@ impl FromType for ReflectBundle { .for_each(|field| apply_or_insert_field::(entity, field, registry)), _ => panic!( "expected bundle `{}` to be named struct or tuple", - std::any::type_name::() + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), ), } }, @@ -188,14 +190,14 @@ fn insert_field( if world.components().get_id(TypeId::of::()).is_some() { panic!( "no `ReflectComponent` registration found for `{}`", - field.type_name() + field.reflect_type_path(), ); }; }); panic!( "no `ReflectBundle` registration found for `{}`", - field.type_name() + field.reflect_type_path(), ) } } @@ -214,14 +216,14 @@ fn apply_or_insert_field( if world.components().get_id(TypeId::of::()).is_some() { panic!( "no `ReflectComponent` registration found for `{}`", - field.type_name() + field.reflect_type_path(), ); }; }); panic!( "no `ReflectBundle` registration found for `{}`", - field.type_name() + field.reflect_type_path(), ) } } diff --git a/crates/bevy_ecs/src/reflect/entity_commands.rs b/crates/bevy_ecs/src/reflect/entity_commands.rs index ca61ebb85c866..f0856da378d52 100644 --- a/crates/bevy_ecs/src/reflect/entity_commands.rs +++ b/crates/bevy_ecs/src/reflect/entity_commands.rs @@ -161,10 +161,10 @@ impl<'w, 's, 'a> ReflectCommandExt for EntityCommands<'w, 's, 'a> { self } - fn remove_reflect(&mut self, component_type_name: impl Into>) -> &mut Self { + fn remove_reflect(&mut self, component_type_path: impl Into>) -> &mut Self { self.commands.add(RemoveReflect { entity: self.entity, - component_type_name: component_type_name.into(), + component_type_path: component_type_path.into(), }); self } @@ -189,15 +189,15 @@ fn insert_reflect( type_registry: &TypeRegistry, component: Box, ) { - let type_info = component.type_name(); + let type_info = component.reflect_type_path(); let Some(mut entity) = world.get_entity_mut(entity) else { - panic!("error[B0003]: Could not insert a reflected component (of type {}) for entity {entity:?} because it doesn't exist in this World.", component.type_name()); + panic!("error[B0003]: Could not insert a reflected component (of type {}) for entity {entity:?} because it doesn't exist in this World.", component.reflect_type_path()); }; - let Some(type_registration) = type_registry.get_with_name(type_info) else { - panic!("Could not get type registration (for component type {}) because it doesn't exist in the TypeRegistry.", component.type_name()); + let Some(type_registration) = type_registry.get_with_type_path(type_info) else { + panic!("Could not get type registration (for component type {}) because it doesn't exist in the TypeRegistry.", component.reflect_type_path()); }; let Some(reflect_component) = type_registration.data::() else { - panic!("Could not get ReflectComponent data (for component type {}) because it doesn't exist in this TypeRegistration.", component.type_name()); + panic!("Could not get ReflectComponent data (for component type {}) because it doesn't exist in this TypeRegistration.", component.reflect_type_path()); }; reflect_component.insert(&mut entity, &*component); } @@ -246,12 +246,12 @@ fn remove_reflect( world: &mut World, entity: Entity, type_registry: &TypeRegistry, - component_type_name: Cow<'static, str>, + component_type_path: Cow<'static, str>, ) { let Some(mut entity) = world.get_entity_mut(entity) else { return; }; - let Some(type_registration) = type_registry.get_with_name(&component_type_name) else { + let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else { return; }; let Some(reflect_component) = type_registration.data::() else { @@ -269,7 +269,7 @@ pub struct RemoveReflect { pub entity: Entity, /// The [`Component`](crate::component::Component) type name that will be used to remove a component /// of the same type from the entity. - pub component_type_name: Cow<'static, str>, + pub component_type_path: Cow<'static, str>, } impl Command for RemoveReflect { @@ -279,7 +279,7 @@ impl Command for RemoveReflect { world, self.entity, ®istry.read(), - self.component_type_name, + self.component_type_path, ); } } @@ -413,7 +413,7 @@ mod tests { commands .entity(entity) - .remove_reflect(boxed_reflect_component_a.type_name().to_owned()); + .remove_reflect(boxed_reflect_component_a.reflect_type_path().to_owned()); system_state.apply(&mut world); assert_eq!(world.entity(entity).get::(), None); @@ -443,7 +443,7 @@ mod tests { commands .entity(entity) .remove_reflect_with_registry::( - boxed_reflect_component_a.type_name().to_owned(), + boxed_reflect_component_a.reflect_type_path().to_owned(), ); system_state.apply(&mut world); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 8f3c5d9a7e0e4..0e5ef21dcaf69 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -34,14 +34,14 @@ pub(crate) enum ReflectDerive<'a> { /// // traits /// // |----------------------------------------| /// #[reflect(PartialEq, Serialize, Deserialize, Default)] -/// // type_name generics +/// // type_path generics /// // |-------------------||----------| /// struct ThingThatImReflecting {/* ... */} /// ``` pub(crate) struct ReflectMeta<'a> { /// The registered traits for this type. traits: ReflectTraits, - /// The name of this type. + /// The path to this type. type_path: ReflectTypePath<'a>, /// A cached instance of the path to the `bevy_reflect` crate. bevy_reflect_path: Path, @@ -389,7 +389,7 @@ impl<'a> ReflectMeta<'a> { self.traits.from_reflect_attrs() } - /// The name of this struct. + /// The path to this type. pub fn type_path(&self) -> &ReflectTypePath<'a> { &self.type_path } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index dc84fc6ab61c4..69525bd759210 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -73,7 +73,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) { match #bevy_reflect_path::Enum::variant_name(#ref_value) { #(#variant_names => #fqoption::Some(#variant_constructors),)* - name => panic!("variant with name `{}` does not exist on enum `{}`", name, ::core::any::type_name::()), + name => panic!("variant with name `{}` does not exist on enum `{}`", name, ::type_path()), } } else { #FQOption::None diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index aad84808ccc3a..8eec84fcac678 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -58,20 +58,18 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream } }); - let string_name = enum_path.get_ident().unwrap().to_string(); - #[cfg(feature = "documentation")] let info_generator = { let doc = reflect_enum.meta().doc(); quote! { - #bevy_reflect_path::EnumInfo::new::(#string_name, &variants).with_docs(#doc) + #bevy_reflect_path::EnumInfo::new::(&variants).with_docs(#doc) } }; #[cfg(not(feature = "documentation"))] let info_generator = { quote! { - #bevy_reflect_path::EnumInfo::new::(#string_name, &variants) + #bevy_reflect_path::EnumInfo::new::(&variants) } }; @@ -188,11 +186,6 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream } impl #impl_generics #bevy_reflect_path::Reflect for #enum_path #ty_generics #where_reflect_clause { - #[inline] - fn type_name(&self) -> &str { - ::core::any::type_name::() - } - #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { #FQOption::Some(::type_info()) @@ -264,11 +257,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream #(#variant_names => { *self = #variant_constructors })* - name => panic!("variant with name `{}` does not exist on enum `{}`", name, ::core::any::type_name::()), + name => panic!("variant with name `{}` does not exist on enum `{}`", name, ::type_path()), } } } else { - panic!("`{}` is not an enum", #bevy_reflect_path::Reflect::type_name(#ref_value)); + panic!("`{}` is not an enum", #bevy_reflect_path::DynamicTypePath::reflect_type_path(#ref_value)); } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index a4d6e92007012..60a5c14cbc369 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -63,20 +63,18 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS } }; - let string_name = struct_path.get_ident().unwrap().to_string(); - #[cfg(feature = "documentation")] let info_generator = { let doc = reflect_struct.meta().doc(); quote! { - #bevy_reflect_path::StructInfo::new::(#string_name, &fields).with_docs(#doc) + #bevy_reflect_path::StructInfo::new::(&fields).with_docs(#doc) } }; #[cfg(not(feature = "documentation"))] let info_generator = { quote! { - #bevy_reflect_path::StructInfo::new::(#string_name, &fields) + #bevy_reflect_path::StructInfo::new::(&fields) } }; @@ -163,11 +161,6 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS } impl #impl_generics #bevy_reflect_path::Reflect for #struct_path #ty_generics #where_reflect_clause { - #[inline] - fn type_name(&self) -> &str { - ::core::any::type_name::() - } - #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { #FQOption::Some(::type_info()) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index 4e39a034e1347..ed507f3714d10 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -57,20 +57,18 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: } }; - let string_name = struct_path.get_ident().unwrap().to_string(); - #[cfg(feature = "documentation")] let info_generator = { let doc = reflect_struct.meta().doc(); quote! { - #bevy_reflect_path::TupleStructInfo::new::(#string_name, &fields).with_docs(#doc) + #bevy_reflect_path::TupleStructInfo::new::(&fields).with_docs(#doc) } }; #[cfg(not(feature = "documentation"))] let info_generator = { quote! { - #bevy_reflect_path::TupleStructInfo::new::(#string_name, &fields) + #bevy_reflect_path::TupleStructInfo::new::(&fields) } }; @@ -133,11 +131,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: } impl #impl_generics #bevy_reflect_path::Reflect for #struct_path #ty_generics #where_reflect_clause { - #[inline] - fn type_name(&self) -> &str { - ::core::any::type_name::() - } - #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { #FQOption::Some(::type_info()) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 88b8479737cec..a9f43cbbfede9 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -45,11 +45,6 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { #typed_impl impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause { - #[inline] - fn type_name(&self) -> &str { - ::core::any::type_name::() - } - #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { #FQOption::Some(::type_info()) @@ -96,7 +91,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { if let #FQOption::Some(value) = ::downcast_ref::(value) { *self = #FQClone::clone(value); } else { - panic!("Value is not {}.", ::core::any::type_name::()); + panic!("Value is not {}.", ::type_path()); } } diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 0959826fefd51..befb87c6d78e2 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,6 +1,6 @@ use crate::{ self as bevy_reflect, utility::reflect_hasher, Reflect, ReflectMut, ReflectOwned, ReflectRef, - TypeInfo, + TypeInfo, TypePath, TypePathTable, }; use bevy_reflect_derive::impl_type_path; use std::{ @@ -77,9 +77,9 @@ pub trait Array: Reflect { /// A container for compile-time array info. #[derive(Clone, Debug)] pub struct ArrayInfo { - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, - item_type_name: &'static str, + item_type_path: TypePathTable, item_type_id: TypeId, capacity: usize, #[cfg(feature = "documentation")] @@ -93,11 +93,11 @@ impl ArrayInfo { /// /// * `capacity`: The maximum capacity of the underlying array. /// - pub fn new(capacity: usize) -> Self { + pub fn new(capacity: usize) -> Self { Self { - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), - item_type_name: std::any::type_name::(), + item_type_path: TypePathTable::of::(), item_type_id: TypeId::of::(), capacity, #[cfg(feature = "documentation")] @@ -116,11 +116,21 @@ impl ArrayInfo { self.capacity } - /// The [type name] of the array. + /// A representation of the type path of the array. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the array. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the array. @@ -133,11 +143,11 @@ impl ArrayInfo { TypeId::of::() == self.type_id } - /// The [type name] of the array item. + /// A representation of the type path of the array item. /// - /// [type name]: std::any::type_name - pub fn item_type_name(&self) -> &'static str { - self.item_type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn item_type_path_table(&self) -> &TypePathTable { + &self.item_type_path } /// The [`TypeId`] of the array item. @@ -213,13 +223,6 @@ impl DynamicArray { } impl Reflect for DynamicArray { - #[inline] - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_else(|| std::any::type_name::()) - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 32b752836da56..2895d3c9a8522 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -288,13 +288,6 @@ impl Enum for DynamicEnum { } impl Reflect for DynamicEnum { - #[inline] - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_default() - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type @@ -376,7 +369,7 @@ impl Reflect for DynamicEnum { self.set_variant(value.variant_name(), dyn_variant); } } else { - panic!("`{}` is not an enum", value.type_name()); + panic!("`{}` is not an enum", value.reflect_type_path()); } } diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index a4b9bec2a4512..09158754ae029 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,4 +1,4 @@ -use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; +use crate::{DynamicEnum, Reflect, TypePath, TypePathTable, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::slice::Iter; @@ -126,15 +126,14 @@ pub trait Enum: Reflect { } /// Returns the full path to the current variant. fn variant_path(&self) -> String { - format!("{}::{}", self.type_name(), self.variant_name()) + format!("{}::{}", self.reflect_type_path(), self.variant_name()) } } /// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo). #[derive(Clone, Debug)] pub struct EnumInfo { - name: &'static str, - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, variants: Box<[VariantInfo]>, variant_names: Box<[&'static str]>, @@ -148,10 +147,9 @@ impl EnumInfo { /// /// # Arguments /// - /// * `name`: The name of this enum (_without_ generics or lifetimes) /// * `variants`: The variants of this enum in the order they are defined /// - pub fn new(name: &'static str, variants: &[VariantInfo]) -> Self { + pub fn new(variants: &[VariantInfo]) -> Self { let variant_indices = variants .iter() .enumerate() @@ -161,8 +159,7 @@ impl EnumInfo { let variant_names = variants.iter().map(|variant| variant.name()).collect(); Self { - name, - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), variants: variants.to_vec().into_boxed_slice(), variant_names, @@ -204,7 +201,7 @@ impl EnumInfo { /// /// This does _not_ check if the given variant exists. pub fn variant_path(&self, name: &str) -> String { - format!("{}::{name}", self.type_name()) + format!("{}::{name}", self.type_path()) } /// Checks if a variant with the given name exists within this enum. @@ -222,20 +219,21 @@ impl EnumInfo { self.variants.len() } - /// The name of the enum. + /// A representation of the type path of the value. /// - /// This does _not_ include any generics or lifetimes. - /// - /// For example, `foo::bar::Baz<'a, T>` would simply be `Baz`. - pub fn name(&self) -> &'static str { - self.name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path } - /// The [type name] of the enum. + /// The [stable, full type path] of the value. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the enum. diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index a7f62de51807b..b78af06f86729 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -25,7 +25,15 @@ mod tests { let info = MyEnum::type_info(); if let TypeInfo::Enum(info) = info { assert!(info.is::(), "expected type to be `MyEnum`"); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(MyEnum::type_path(), info.type_path()); + assert_eq!(MyEnum::type_path(), info.type_path_table().path()); + assert_eq!(MyEnum::type_ident(), info.type_path_table().ident()); + assert_eq!(MyEnum::module_path(), info.type_path_table().module_path()); + assert_eq!(MyEnum::crate_name(), info.type_path_table().crate_name()); + assert_eq!( + MyEnum::short_type_path(), + info.type_path_table().short_path() + ); // === MyEnum::A === // assert_eq!("A", info.variant_at(0).unwrap().name()); @@ -275,7 +283,7 @@ mod tests { } #[test] - #[should_panic(expected = "`((usize, i32))` is not an enum")] + #[should_panic(expected = "`bevy_reflect::DynamicTuple` is not an enum")] fn applying_non_enum_should_panic() { let mut value = MyEnum::B(0, 0); let mut dyn_tuple = DynamicTuple::default(); diff --git a/crates/bevy_reflect/src/fields.rs b/crates/bevy_reflect/src/fields.rs index 62c5c332adecc..763d04ab2b7e6 100644 --- a/crates/bevy_reflect/src/fields.rs +++ b/crates/bevy_reflect/src/fields.rs @@ -1,11 +1,11 @@ -use crate::Reflect; +use crate::{Reflect, TypePath, TypePathTable}; use std::any::{Any, TypeId}; /// The named field of a reflected struct. #[derive(Clone, Debug)] pub struct NamedField { name: &'static str, - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -13,10 +13,10 @@ pub struct NamedField { impl NamedField { /// Create a new [`NamedField`]. - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str) -> Self { Self { name, - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), #[cfg(feature = "documentation")] docs: None, @@ -34,11 +34,21 @@ impl NamedField { self.name } - /// The [type name] of the field. + /// A representation of the type path of the field. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the field. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the field. @@ -62,17 +72,17 @@ impl NamedField { #[derive(Clone, Debug)] pub struct UnnamedField { index: usize, - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, #[cfg(feature = "documentation")] docs: Option<&'static str>, } impl UnnamedField { - pub fn new(index: usize) -> Self { + pub fn new(index: usize) -> Self { Self { index, - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), #[cfg(feature = "documentation")] docs: None, @@ -90,11 +100,21 @@ impl UnnamedField { self.index } - /// The [type name] of the field. + /// A representation of the type path of the field. + /// + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the field. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the field. diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index 861d0b1d063d1..85de6c6bffd12 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -75,7 +75,7 @@ pub trait FromReflect: Reflect + Sized { /// # Example /// /// ``` -/// # use bevy_reflect::{DynamicTupleStruct, Reflect, ReflectFromReflect, Typed, TypeRegistry}; +/// # use bevy_reflect::{DynamicTupleStruct, Reflect, ReflectFromReflect, Typed, TypeRegistry, TypePath}; /// # #[derive(Reflect, PartialEq, Eq, Debug)] /// # struct Foo(#[reflect(default = "default_value")] usize); /// # fn default_value() -> usize { 123 } @@ -85,7 +85,7 @@ pub trait FromReflect: Reflect + Sized { /// let mut reflected = DynamicTupleStruct::default(); /// reflected.set_represented_type(Some(::type_info())); /// -/// let registration = registry.get_with_name(reflected.type_name()).unwrap(); +/// let registration = registry.get_with_type_path(::type_path()).unwrap(); /// let rfr = registration.data::().unwrap(); /// /// let concrete: Box = rfr.from_reflect(&reflected).unwrap(); diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 1f7bce2b8bcb4..6ff89469afcff 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -11,7 +11,7 @@ use crate::{ impl List for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn get(&self, index: usize) -> Option<&dyn Reflect> { if index < SmallVec::len(self) { @@ -34,7 +34,7 @@ where ::Item::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to insert invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }) }); @@ -50,7 +50,7 @@ where ::Item::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to push invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }) }); @@ -78,12 +78,8 @@ where impl Reflect for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -144,7 +140,7 @@ where impl Typed for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); @@ -152,11 +148,11 @@ where } } -impl_type_path!(::smallvec::SmallVec); +impl_type_path!(::smallvec::SmallVec); impl FromReflect for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::List(ref_list) = reflect.reflect_ref() { @@ -173,7 +169,7 @@ where impl GetTypeRegistration for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 0f594a7f81235..c3cc07432cd83 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -79,7 +79,8 @@ impl_reflect_value!(isize( )); impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize, Default)); impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize, Default)); -impl_reflect_value!(String( +impl_type_path!(str); +impl_reflect_value!(::alloc::string::String( Debug, Hash, PartialEq, @@ -232,7 +233,7 @@ macro_rules! impl_reflect_for_veclike { T::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to insert invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }) }); @@ -247,7 +248,7 @@ macro_rules! impl_reflect_for_veclike { let value = T::take_from_reflect(value).unwrap_or_else(|value| { panic!( "Attempted to push invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }); $push(self, value); @@ -276,10 +277,6 @@ macro_rules! impl_reflect_for_veclike { } impl Reflect for $ty { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -349,7 +346,7 @@ macro_rules! impl_reflect_for_veclike { } } - impl_type_path!($ty where T: FromReflect); + impl_type_path!($ty); impl GetTypeRegistration for $ty { fn get_type_registration() -> TypeRegistration { @@ -448,7 +445,10 @@ macro_rules! impl_reflect_for_hashmap { dynamic_map.set_represented_type(self.get_represented_type_info()); for (k, v) in self { let key = K::from_reflect(k).unwrap_or_else(|| { - panic!("Attempted to clone invalid key of type {}.", k.type_name()) + panic!( + "Attempted to clone invalid key of type {}.", + k.reflect_type_path() + ) }); dynamic_map.insert_boxed(Box::new(key), v.clone_value()); } @@ -463,13 +463,13 @@ macro_rules! impl_reflect_for_hashmap { let key = K::take_from_reflect(key).unwrap_or_else(|key| { panic!( "Attempted to insert invalid key of type {}.", - key.type_name() + key.reflect_type_path() ) }); let value = V::take_from_reflect(value).unwrap_or_else(|value| { panic!( "Attempted to insert invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }); self.insert(key, value) @@ -494,10 +494,6 @@ macro_rules! impl_reflect_for_hashmap { V: FromReflect + TypePath, S: TypePath + BuildHasher + Send + Sync, { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -607,23 +603,11 @@ macro_rules! impl_reflect_for_hashmap { impl_reflect_for_hashmap!(::std::collections::HashMap); impl_type_path!(::std::collections::hash_map::RandomState); -impl_type_path!( - ::std::collections::HashMap - where - K: FromReflect + Eq + Hash + ?Sized, - V: FromReflect + ?Sized, - S: BuildHasher + Send + Sync + 'static, -); +impl_type_path!(::std::collections::HashMap); impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap); impl_type_path!(::bevy_utils::hashbrown::hash_map::DefaultHashBuilder); -impl_type_path!( - ::bevy_utils::hashbrown::HashMap - where - K: FromReflect + Eq + Hash + ?Sized, - V: FromReflect + ?Sized, - S: BuildHasher + Send + Sync + 'static, -); +impl_type_path!(::bevy_utils::hashbrown::HashMap); impl Array for [T; N] { #[inline] @@ -655,11 +639,6 @@ impl Array for [T; N] { } impl Reflect for [T; N] { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -757,7 +736,7 @@ impl Typed for [T; N] { } } -impl TypePath for [T; N] { +impl TypePath for [T; N] { fn type_path() -> &'static str { static CELL: GenericTypePathCell = GenericTypePathCell::new(); CELL.get_or_insert::(|| format!("[{t}; {N}]", t = T::type_path())) @@ -871,11 +850,6 @@ impl Enum for Option { } impl Reflect for Option { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) @@ -929,7 +903,7 @@ impl Reflect for Option { .unwrap_or_else(|| { panic!( "Field in `Some` variant of {} should exist", - std::any::type_name::>() + Self::type_path() ) }) .clone_value(), @@ -937,8 +911,8 @@ impl Reflect for Option { .unwrap_or_else(|_| { panic!( "Field in `Some` variant of {} should be of type {}", - std::any::type_name::>(), - std::any::type_name::() + Self::type_path(), + T::type_path() ) }); *self = Some(field); @@ -946,7 +920,7 @@ impl Reflect for Option { "None" => { *self = None; } - _ => panic!("Enum is not a {}.", std::any::type_name::()), + _ => panic!("Enum is not a {}.", Self::type_path()), } } } @@ -995,7 +969,7 @@ impl FromReflect for Option { .unwrap_or_else(|| { panic!( "Field in `Some` variant of {} should exist", - std::any::type_name::>() + Option::::type_path() ) }) .clone_value(), @@ -1003,8 +977,8 @@ impl FromReflect for Option { .unwrap_or_else(|_| { panic!( "Field in `Some` variant of {} should be of type {}", - std::any::type_name::>(), - std::any::type_name::() + Option::::type_path(), + T::type_path() ) }); Some(Some(field)) @@ -1013,7 +987,7 @@ impl FromReflect for Option { name => panic!( "variant with name `{}` does not exist on enum `{}`", name, - std::any::type_name::() + Self::type_path() ), } } else { @@ -1029,21 +1003,38 @@ impl Typed for Option { let none_variant = VariantInfo::Unit(UnitVariantInfo::new("None")); let some_variant = VariantInfo::Tuple(TupleVariantInfo::new("Some", &[UnnamedField::new::(0)])); - TypeInfo::Enum(EnumInfo::new::( - "Option", - &[none_variant, some_variant], - )) + TypeInfo::Enum(EnumInfo::new::(&[none_variant, some_variant])) }) } } -impl_type_path!(::core::option::Option); +impl_type_path!(::core::option::Option); -impl Reflect for Cow<'static, str> { - fn type_name(&self) -> &str { - std::any::type_name::() +impl TypePath for &'static T { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("&{}", T::type_path())) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("&{}", T::short_type_path())) + } +} + +impl TypePath for &'static mut T { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("&mut {}", T::type_path())) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("&mut {}", T::short_type_path())) } +} +impl Reflect for Cow<'static, str> { fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -1077,7 +1068,7 @@ impl Reflect for Cow<'static, str> { if let Some(value) = value.downcast_ref::() { *self = value.clone(); } else { - panic!("Value is not a {}.", std::any::type_name::()); + panic!("Value is not a {}.", Self::type_path()); } } @@ -1126,30 +1117,6 @@ impl Typed for Cow<'static, str> { } } -impl TypePath for Cow<'static, str> { - fn type_path() -> &'static str { - static CELL: GenericTypePathCell = GenericTypePathCell::new(); - CELL.get_or_insert::(|| "std::borrow::Cow::".to_owned()) - } - - fn short_type_path() -> &'static str { - static CELL: GenericTypePathCell = GenericTypePathCell::new(); - CELL.get_or_insert::(|| "Cow".to_owned()) - } - - fn type_ident() -> Option<&'static str> { - Some("Cow") - } - - fn crate_name() -> Option<&'static str> { - Some("std") - } - - fn module_path() -> Option<&'static str> { - Some("std::borrow") - } -} - impl GetTypeRegistration for Cow<'static, str> { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); @@ -1171,8 +1138,6 @@ impl FromReflect for Cow<'static, str> { } } -impl PathOnly for [T] where [T]: ToOwned {} - impl TypePath for [T] where [T]: ToOwned, @@ -1188,8 +1153,6 @@ where } } -impl PathOnly for T {} - impl List for Cow<'static, [T]> { fn get(&self, index: usize) -> Option<&dyn Reflect> { self.as_ref().get(index).map(|x| x as &dyn Reflect) @@ -1221,7 +1184,7 @@ impl List for Cow<'static, [T]> { T::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to insert invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }) }); @@ -1236,7 +1199,7 @@ impl List for Cow<'static, [T]> { let value = T::take_from_reflect(value).unwrap_or_else(|value| { panic!( "Attempted to push invalid value of type {}.", - value.type_name() + value.reflect_type_path() ) }); self.to_mut().push(value); @@ -1250,10 +1213,6 @@ impl List for Cow<'static, [T]> { } impl Reflect for Cow<'static, [T]> { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn into_any(self: Box) -> Box { self } @@ -1344,10 +1303,6 @@ impl FromReflect for Cow<'static, [T]> { } impl Reflect for &'static Path { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -1381,7 +1336,7 @@ impl Reflect for &'static Path { if let Some(&value) = value.downcast_ref::() { *self = value; } else { - panic!("Value is not a {}.", std::any::type_name::()); + panic!("Value is not a {}.", Self::type_path()); } } @@ -1430,18 +1385,6 @@ impl Typed for &'static Path { } } -impl TypePath for &'static Path { - fn type_path() -> &'static str { - static CELL: GenericTypePathCell = GenericTypePathCell::new(); - CELL.get_or_insert::(|| "&std::path::Path".to_owned()) - } - - fn short_type_path() -> &'static str { - static CELL: GenericTypePathCell = GenericTypePathCell::new(); - CELL.get_or_insert::(|| "&Path".to_owned()) - } -} - impl GetTypeRegistration for &'static Path { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); @@ -1457,10 +1400,6 @@ impl FromReflect for &'static Path { } impl Reflect for Cow<'static, Path> { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -1494,7 +1433,7 @@ impl Reflect for Cow<'static, Path> { if let Some(value) = value.downcast_ref::() { *self = value.clone(); } else { - panic!("Value is not a {}.", std::any::type_name::()); + panic!("Value is not a {}.", Self::type_path()); } } @@ -1547,10 +1486,8 @@ impl Typed for Cow<'static, Path> { } } -trait PathOnly: ToOwned {} -impl PathOnly for Path {} -impl_type_path!(::alloc::borrow::Cow<'a: 'static, T: PathOnly + ?Sized>); impl_type_path!(::std::path::Path); +impl_type_path!(::alloc::borrow::Cow<'a: 'static, T: ToOwned + ?Sized>); impl FromReflect for Cow<'static, Path> { fn from_reflect(reflect: &dyn Reflect) -> Option { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index b8ba8da5dffdd..53304fb65b428 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -335,7 +335,7 @@ //! The general entry point are the "untyped" versions of these structs. //! These will automatically extract the type information and pass them into their respective "typed" version. //! -//! The output of the `ReflectSerializer` will be a map, where the key is the [type name] +//! The output of the `ReflectSerializer` will be a map, where the key is the [type path] //! and the value is the serialized data. //! The `TypedReflectSerializer` will simply output the serialized data. //! @@ -456,7 +456,7 @@ //! [`TypedReflectDeserializer`]: serde::TypedReflectDeserializer //! [registry]: TypeRegistry //! [type information]: TypeInfo -//! [type name]: Reflect::type_name +//! [type path]: TypePath //! [type registry]: TypeRegistry //! [`bevy_math`]: https://docs.rs/bevy_math/latest/bevy_math/ //! [`glam`]: https://docs.rs/glam/latest/glam/ @@ -1131,28 +1131,28 @@ mod tests { } #[test] - fn dynamic_names() { + fn not_dynamic_names() { let list = Vec::::new(); let dyn_list = list.clone_dynamic(); - assert_eq!(dyn_list.type_name(), std::any::type_name::>()); + assert_ne!(dyn_list.reflect_type_path(), Vec::::type_path()); let array = [b'0'; 4]; let dyn_array = array.clone_dynamic(); - assert_eq!(dyn_array.type_name(), std::any::type_name::<[u8; 4]>()); + assert_ne!(dyn_array.reflect_type_path(), <[u8; 4]>::type_path()); let map = HashMap::::default(); let dyn_map = map.clone_dynamic(); - assert_eq!( - dyn_map.type_name(), - std::any::type_name::>() + assert_ne!( + dyn_map.reflect_type_path(), + HashMap::::type_path() ); let tuple = (0usize, "1".to_string(), 2.0f32); let mut dyn_tuple = tuple.clone_dynamic(); dyn_tuple.insert::(3); - assert_eq!( - dyn_tuple.type_name(), - std::any::type_name::<(usize, String, f32, usize)>() + assert_ne!( + dyn_tuple.reflect_type_path(), + <(usize, String, f32, usize)>::type_path() ); #[derive(Reflect)] @@ -1161,18 +1161,27 @@ mod tests { } let struct_ = TestStruct { a: 0 }; let dyn_struct = struct_.clone_dynamic(); - assert_eq!(dyn_struct.type_name(), std::any::type_name::()); + assert_ne!(dyn_struct.reflect_type_path(), TestStruct::type_path()); #[derive(Reflect)] struct TestTupleStruct(usize); let tuple_struct = TestTupleStruct(0); let dyn_tuple_struct = tuple_struct.clone_dynamic(); - assert_eq!( - dyn_tuple_struct.type_name(), - std::any::type_name::() + assert_ne!( + dyn_tuple_struct.reflect_type_path(), + TestTupleStruct::type_path() ); } + macro_rules! assert_type_paths { + ($($ty:ty => $long:literal, $short:literal,)*) => { + $( + assert_eq!(<$ty as TypePath>::type_path(), $long); + assert_eq!(<$ty as TypePath>::short_type_path(), $short); + )* + }; + } + #[test] fn reflect_type_path() { #[derive(TypePath)] @@ -1214,62 +1223,57 @@ mod tests { struct MacroNameG(PhantomData); impl_type_path!((in my_alias as MyMacroNameG) MacroNameG); - assert_eq!(Derive::type_path(), "bevy_reflect::tests::Derive"); - assert_eq!(DerivePath::type_path(), "my_alias::DerivePath"); - assert_eq!(DerivePathName::type_path(), "my_alias::MyDerivePathName"); - - assert_eq!( - DeriveG::::type_path(), - "bevy_reflect::tests::DeriveG" - ); - assert_eq!( - DerivePathG::::type_path(), - "my_alias::DerivePathG" - ); - assert_eq!( - DerivePathNameG::::type_path(), - "my_alias::MyDerivePathNameG" - ); + assert_type_paths! { + Derive => "bevy_reflect::tests::Derive", "Derive", + DerivePath => "my_alias::DerivePath", "DerivePath", + DerivePathName => "my_alias::MyDerivePathName", "MyDerivePathName", + DeriveG => "bevy_reflect::tests::DeriveG", "DeriveG", + DerivePathG => "my_alias::DerivePathG", "DerivePathG", + DerivePathNameG => "my_alias::MyDerivePathNameG", "MyDerivePathNameG", + Macro => "my_alias::Macro", "Macro", + MacroName => "my_alias::MyMacroName", "MyMacroName", + MacroG => "my_alias::MacroG", "MacroG", + MacroNameG => "my_alias::MyMacroNameG", "MyMacroNameG", + } + } - assert_eq!(Macro::type_path(), "my_alias::Macro"); - assert_eq!(MacroName::type_path(), "my_alias::MyMacroName"); - assert_eq!( - MacroG::::type_path(), - "my_alias::MacroG" - ); - assert_eq!( - MacroNameG::::type_path(), - "my_alias::MyMacroNameG" - ); + #[test] + fn std_type_paths() { + #[derive(Clone)] + struct Type; - assert_eq!(Derive::short_type_path(), "Derive"); - assert_eq!(DerivePath::short_type_path(), "DerivePath"); - assert_eq!(DerivePathName::short_type_path(), "MyDerivePathName"); + impl TypePath for Type { + fn type_path() -> &'static str { + // for brevity in tests + "Long" + } - assert_eq!(DeriveG::::short_type_path(), "DeriveG"); - assert_eq!( - DerivePathG::::short_type_path(), - "DerivePathG" - ); - assert_eq!( - DerivePathNameG::::short_type_path(), - "MyDerivePathNameG" - ); + fn short_type_path() -> &'static str { + "Short" + } + } - assert_eq!(Macro::short_type_path(), "Macro"); - assert_eq!(MacroName::short_type_path(), "MyMacroName"); - assert_eq!(MacroG::::short_type_path(), "MacroG"); - assert_eq!( - MacroNameG::::short_type_path(), - "MyMacroNameG" - ); + assert_type_paths! { + u8 => "u8", "u8", + Type => "Long", "Short", + &Type => "&Long", "&Short", + [Type] => "[Long]", "[Short]", + &[Type] => "&[Long]", "&[Short]", + [Type; 0] => "[Long; 0]", "[Short; 0]", + [Type; 100] => "[Long; 100]", "[Short; 100]", + () => "()", "()", + (Type,) => "(Long,)", "(Short,)", + (Type, Type) => "(Long, Long)", "(Short, Short)", + (Type, Type, Type) => "(Long, Long, Long)", "(Short, Short, Short)", + Cow<'static, Type> => "alloc::borrow::Cow", "Cow", + } } #[test] fn reflect_type_info() { // TypeInfo let info = i32::type_info(); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(i32::type_path(), info.type_path()); assert_eq!(std::any::TypeId::of::(), info.type_id()); // TypeInfo (unsized) @@ -1293,21 +1297,15 @@ mod tests { let info = MyStruct::type_info(); if let TypeInfo::Struct(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!( - std::any::type_name::(), - info.field("foo").unwrap().type_name() - ); + assert_eq!(MyStruct::type_path(), info.type_path()); + assert_eq!(i32::type_path(), info.field("foo").unwrap().type_path()); assert_eq!( std::any::TypeId::of::(), info.field("foo").unwrap().type_id() ); assert!(info.field("foo").unwrap().is::()); assert_eq!("foo", info.field("foo").unwrap().name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path()); } else { panic!("Expected `TypeInfo::Struct`"); } @@ -1326,19 +1324,10 @@ mod tests { let info = >::type_info(); if let TypeInfo::Struct(info) = info { assert!(info.is::>()); - assert_eq!( - std::any::type_name::>(), - info.type_name() - ); - assert_eq!( - std::any::type_name::(), - info.field("foo").unwrap().type_name() - ); + assert_eq!(MyGenericStruct::::type_path(), info.type_path()); + assert_eq!(i32::type_path(), info.field("foo").unwrap().type_path()); assert_eq!("foo", info.field("foo").unwrap().name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path()); } else { panic!("Expected `TypeInfo::Struct`"); } @@ -1357,11 +1346,8 @@ mod tests { let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(MyTupleStruct::type_path(), info.type_path()); + assert_eq!(i32::type_path(), info.field_at(1).unwrap().type_path()); assert!(info.field_at(1).unwrap().is::()); } else { panic!("Expected `TypeInfo::TupleStruct`"); @@ -1373,11 +1359,8 @@ mod tests { let info = MyTuple::type_info(); if let TypeInfo::Tuple(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(MyTuple::type_path(), info.type_path()); + assert_eq!(f32::type_path(), info.field_at(1).unwrap().type_path()); } else { panic!("Expected `TypeInfo::Tuple`"); } @@ -1393,8 +1376,8 @@ mod tests { if let TypeInfo::List(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(MyList::type_path(), info.type_path()); + assert_eq!(usize::type_path(), info.item_type_path_table().path()); } else { panic!("Expected `TypeInfo::List`"); } @@ -1412,8 +1395,8 @@ mod tests { if let TypeInfo::List(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(MySmallVec::type_path(), info.type_path()); + assert_eq!(String::type_path(), info.item_type_path_table().path()); } else { panic!("Expected `TypeInfo::List`"); } @@ -1431,8 +1414,8 @@ mod tests { if let TypeInfo::Array(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(MyArray::type_path(), info.type_path()); + assert_eq!(usize::type_path(), info.item_type_path_table().path()); assert_eq!(3, info.capacity()); } else { panic!("Expected `TypeInfo::Array`"); @@ -1448,7 +1431,7 @@ mod tests { let info = MyCowStr::type_info(); if let TypeInfo::Value(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(std::any::type_name::(), info.type_path()); } else { panic!("Expected `TypeInfo::Value`"); } @@ -1464,8 +1447,11 @@ mod tests { if let TypeInfo::List(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(std::any::type_name::(), info.type_path()); + assert_eq!( + std::any::type_name::(), + info.item_type_path_table().path() + ); } else { panic!("Expected `TypeInfo::List`"); } @@ -1482,9 +1468,9 @@ mod tests { assert!(info.is::()); assert!(info.key_is::()); assert!(info.value_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.key_type_name()); - assert_eq!(std::any::type_name::(), info.value_type_name()); + assert_eq!(MyMap::type_path(), info.type_path()); + assert_eq!(usize::type_path(), info.key_type_path_table().path()); + assert_eq!(f32::type_path(), info.value_type_path_table().path()); } else { panic!("Expected `TypeInfo::Map`"); } @@ -1499,7 +1485,7 @@ mod tests { let info = MyValue::type_info(); if let TypeInfo::Value(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(MyValue::type_path(), info.type_path()); } else { panic!("Expected `TypeInfo::Value`"); } @@ -1793,7 +1779,7 @@ mod tests { let reflected: &dyn Reflect = &test; let expected = r#" -bevy_reflect::tests::should_reflect_debug::Test { +bevy_reflect::tests::Test { value: 123, list: [ "A", @@ -1808,10 +1794,10 @@ bevy_reflect::tests::should_reflect_debug::Test { map: { 123: 1.23, }, - a_struct: bevy_reflect::tests::should_reflect_debug::SomeStruct { + a_struct: bevy_reflect::tests::SomeStruct { foo: "A Struct!", }, - a_tuple_struct: bevy_reflect::tests::should_reflect_debug::SomeTupleStruct( + a_tuple_struct: bevy_reflect::tests::SomeTupleStruct( "A Tuple Struct!", ), enum_unit: A, @@ -1941,7 +1927,10 @@ bevy_reflect::tests::should_reflect_debug::Test { registry.register::>(); let registration = registry.get(TypeId::of::>()).unwrap(); - assert_eq!("Foo", registration.short_name()); + assert_eq!( + "Foo", + registration.type_info().type_path_table().short_path() + ); } #[cfg(feature = "glam")] @@ -1964,7 +1953,7 @@ bevy_reflect::tests::should_reflect_debug::Test { let output = to_string_pretty(&ser, config).unwrap(); let expected = r#" { - "glam::f32::vec3::Vec3": ( + "glam::Vec3": ( x: 12.0, y: 3.0, z: -6.9, @@ -1978,7 +1967,7 @@ bevy_reflect::tests::should_reflect_debug::Test { fn vec3_deserialization() { let data = r#" { - "glam::f32::vec3::Vec3": ( + "glam::Vec3": ( x: 12.0, y: 3.0, z: -6.9, diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 5eb8c7885e813..ed129b803be3b 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -7,6 +7,7 @@ use bevy_reflect_derive::impl_type_path; use crate::utility::reflect_hasher; use crate::{ self as bevy_reflect, FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + TypePath, TypePathTable, }; /// A trait used to power [list-like] operations via [reflection]. @@ -108,9 +109,9 @@ pub trait List: Reflect { /// A container for compile-time list info. #[derive(Clone, Debug)] pub struct ListInfo { - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, - item_type_name: &'static str, + item_type_path: TypePathTable, item_type_id: TypeId, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -118,11 +119,11 @@ pub struct ListInfo { impl ListInfo { /// Create a new [`ListInfo`]. - pub fn new() -> Self { + pub fn new() -> Self { Self { - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), - item_type_name: std::any::type_name::(), + item_type_path: TypePathTable::of::(), item_type_id: TypeId::of::(), #[cfg(feature = "documentation")] docs: None, @@ -135,11 +136,21 @@ impl ListInfo { Self { docs, ..self } } - /// The [type name] of the list. + /// A representation of the type path of the list. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the list. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the list. @@ -152,11 +163,11 @@ impl ListInfo { TypeId::of::() == self.type_id } - /// The [type name] of the list item. + /// A representation of the type path of the list item. /// - /// [type name]: std::any::type_name - pub fn item_type_name(&self) -> &'static str { - self.item_type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn item_type_path_table(&self) -> &TypePathTable { + &self.item_type_path } /// The [`TypeId`] of the list item. @@ -263,13 +274,6 @@ impl List for DynamicList { } impl Reflect for DynamicList { - #[inline] - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_else(|| std::any::type_name::()) - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 0ddc85a562763..5ed7f72870561 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -5,7 +5,10 @@ use std::hash::Hash; use bevy_reflect_derive::impl_type_path; use bevy_utils::{Entry, HashMap}; -use crate::{self as bevy_reflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo}; +use crate::{ + self as bevy_reflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, + TypePathTable, +}; /// A trait used to power [map-like] operations via [reflection]. /// @@ -93,11 +96,11 @@ pub trait Map: Reflect { /// A container for compile-time map info. #[derive(Clone, Debug)] pub struct MapInfo { - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, - key_type_name: &'static str, + key_type_path: TypePathTable, key_type_id: TypeId, - value_type_name: &'static str, + value_type_path: TypePathTable, value_type_id: TypeId, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -105,13 +108,17 @@ pub struct MapInfo { impl MapInfo { /// Create a new [`MapInfo`]. - pub fn new() -> Self { + pub fn new< + TMap: Map + TypePath, + TKey: Hash + Reflect + TypePath, + TValue: Reflect + TypePath, + >() -> Self { Self { - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), - key_type_name: std::any::type_name::(), + key_type_path: TypePathTable::of::(), key_type_id: TypeId::of::(), - value_type_name: std::any::type_name::(), + value_type_path: TypePathTable::of::(), value_type_id: TypeId::of::(), #[cfg(feature = "documentation")] docs: None, @@ -124,11 +131,21 @@ impl MapInfo { Self { docs, ..self } } - /// The [type name] of the map. + /// A representation of the type path of the map. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the map. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the map. @@ -141,11 +158,11 @@ impl MapInfo { TypeId::of::() == self.type_id } - /// The [type name] of the key. + /// A representation of the type path of the key type. /// - /// [type name]: std::any::type_name - pub fn key_type_name(&self) -> &'static str { - self.key_type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn key_type_path_table(&self) -> &TypePathTable { + &self.key_type_path } /// The [`TypeId`] of the key. @@ -158,11 +175,11 @@ impl MapInfo { TypeId::of::() == self.key_type_id } - /// The [type name] of the value. + /// A representation of the type path of the value type. /// - /// [type name]: std::any::type_name - pub fn value_type_name(&self) -> &'static str { - self.value_type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn value_type_path_table(&self) -> &TypePathTable { + &self.value_type_path } /// The [`TypeId`] of the value. @@ -297,12 +314,6 @@ impl Map for DynamicMap { } impl Reflect for DynamicMap { - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_else(|| std::any::type_name::()) - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 9c33ff6753bb5..d36fd5db94f21 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,10 +1,10 @@ use crate::{ array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, tuple_struct_debug, Array, DynamicTypePath, Enum, List, Map, Struct, Tuple, TupleStruct, - TypeInfo, Typed, ValueInfo, + TypeInfo, TypePath, Typed, ValueInfo, }; use std::{ - any::{self, Any, TypeId}, + any::{Any, TypeId}, fmt::Debug, }; @@ -73,8 +73,23 @@ pub enum ReflectOwned { /// [derive macro]: bevy_reflect_derive::Reflect /// [crate-level documentation]: crate pub trait Reflect: DynamicTypePath + Any + Send + Sync { - /// Returns the [type name][std::any::type_name] of the underlying type. - fn type_name(&self) -> &str; + /// Returns the type path of the underlying type. + /// + /// This type path will either be found through [`get_represented_type_info`] + /// or taken from a [`TypePath`] implementation if the former isn't available. + /// + /// This method is deprecated; please consider migrating to one of the above methods. + /// + /// [`get_represented_type_info`]: Reflect::get_represented_type_info + #[deprecated( + since = "0.12.0", + note = "view the method documentation to find alternatives to this method." + )] + fn type_name(&self) -> &str { + self.get_represented_type_info() + .map(|info| info.type_path()) + .unwrap_or_else(|| self.reflect_type_path()) + } /// Returns the [`TypeInfo`] of the type _represented_ by this value. /// @@ -200,10 +215,10 @@ pub trait Reflect: DynamicTypePath + Any + Send + Sync { /// Debug formatter for the value. /// /// Any value that is not an implementor of other `Reflect` subtraits - /// (e.g. [`List`], [`Map`]), will default to the format: `"Reflect(type_name)"`, - /// where `type_name` is the [type name] of the underlying type. + /// (e.g. [`List`], [`Map`]), will default to the format: `"Reflect(type_path)"`, + /// where `type_path` is the [type path] of the underlying type. /// - /// [type name]: Self::type_name + /// [type path]: TypePath::type_path fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.reflect_ref() { ReflectRef::Struct(dyn_struct) => struct_debug(dyn_struct, f), @@ -213,7 +228,7 @@ pub trait Reflect: DynamicTypePath + Any + Send + Sync { ReflectRef::Array(dyn_array) => array_debug(dyn_array, f), ReflectRef::Map(dyn_map) => map_debug(dyn_map, f), ReflectRef::Enum(dyn_enum) => enum_debug(dyn_enum, f), - _ => write!(f, "Reflect({})", self.type_name()), + _ => write!(f, "Reflect({})", self.reflect_type_path()), } } @@ -254,6 +269,19 @@ impl Typed for dyn Reflect { } } +// The following implementation never actually shadows the concrete TypePath implementation. + +// See this playground (https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=589064053f27bc100d90da89c6a860aa). +impl TypePath for dyn Reflect { + fn type_path() -> &'static str { + "dyn bevy_reflect::Reflect" + } + + fn short_type_path() -> &'static str { + "dyn Reflect" + } +} + #[deny(rustdoc::broken_intra_doc_links)] impl dyn Reflect { /// Downcasts the value to type `T`, consuming the trait object. @@ -279,8 +307,10 @@ impl dyn Reflect { /// /// Read `is` for more information on underlying values and represented types. #[inline] - pub fn represents(&self) -> bool { - self.type_name() == any::type_name::() + pub fn represents(&self) -> bool { + self.get_represented_type_info() + .map(|t| t.type_path() == T::type_path()) + .unwrap_or(false) } /// Returns `true` if the underlying value is of type `T`, or `false` diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index a59ae5ec3303b..38f1795186d9a 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -25,13 +25,13 @@ pub trait DeserializeValue { } trait StructLikeInfo { - fn get_name(&self) -> &str; + fn get_path(&self) -> &str; fn get_field(&self, name: &str) -> Option<&NamedField>; fn iter_fields(&self) -> Iter<'_, NamedField>; } trait TupleLikeInfo { - fn get_name(&self) -> &str; + fn get_path(&self) -> &str; fn get_field(&self, index: usize) -> Option<&UnnamedField>; fn get_field_len(&self) -> usize; } @@ -45,8 +45,8 @@ trait Container { } impl StructLikeInfo for StructInfo { - fn get_name(&self) -> &str { - self.type_name() + fn get_path(&self) -> &str { + self.type_path() } fn get_field(&self, name: &str) -> Option<&NamedField> { @@ -68,15 +68,15 @@ impl Container for StructInfo { de::Error::custom(format_args!( "no field at index {} on struct {}", index, - self.type_name(), + self.type_path(), )) })?; - get_registration(field.type_id(), field.type_name(), registry) + get_registration(field.type_id(), field.type_path(), registry) } } impl StructLikeInfo for StructVariantInfo { - fn get_name(&self) -> &str { + fn get_path(&self) -> &str { self.name() } @@ -102,13 +102,13 @@ impl Container for StructVariantInfo { self.name(), )) })?; - get_registration(field.type_id(), field.type_name(), registry) + get_registration(field.type_id(), field.type_path(), registry) } } impl TupleLikeInfo for TupleInfo { - fn get_name(&self) -> &str { - self.type_name() + fn get_path(&self) -> &str { + self.type_path() } fn get_field(&self, index: usize) -> Option<&UnnamedField> { @@ -121,7 +121,7 @@ impl TupleLikeInfo for TupleInfo { } impl TupleLikeInfo for TupleVariantInfo { - fn get_name(&self) -> &str { + fn get_path(&self) -> &str { self.name() } @@ -224,7 +224,7 @@ impl<'de> Visitor<'de> for U32Visitor { /// /// Because the type isn't known ahead of time, the serialized data must take the form of /// a map containing the following entries (in order): -/// 1. `type`: The _full_ [type name] +/// 1. `type`: The _full_ [type path] /// 2. `value`: The serialized value of the reflected type /// /// If the type is already known and the [`TypeInfo`] for it can be retrieved, @@ -234,7 +234,7 @@ impl<'de> Visitor<'de> for U32Visitor { /// [`DynamicStruct`]: crate::DynamicStruct /// [`DynamicList`]: crate::DynamicList /// [`FromReflect`]: crate::FromReflect -/// [type name]: std::any::type_name +/// [type path]: crate::TypePath::type_path pub struct UntypedReflectDeserializer<'a> { registry: &'a TypeRegistry, } @@ -261,11 +261,11 @@ impl<'a, 'de> DeserializeSeed<'de> for UntypedReflectDeserializer<'a> { /// A deserializer for type registrations. /// /// This will return a [`&TypeRegistration`] corresponding to the given type. -/// This deserializer expects a string containing the _full_ [type name] of the +/// This deserializer expects a string containing the _full_ [type path] of the /// type to find the `TypeRegistration` of. /// /// [`&TypeRegistration`]: crate::TypeRegistration -/// [type name]: std::any::type_name +/// [type path]: crate::TypePath::type_path pub struct TypeRegistrationDeserializer<'a> { registry: &'a TypeRegistry, } @@ -292,12 +292,12 @@ impl<'a, 'de> DeserializeSeed<'de> for TypeRegistrationDeserializer<'a> { formatter.write_str("string containing `type` entry for the reflected value") } - fn visit_str(self, type_name: &str) -> Result + fn visit_str(self, type_path: &str) -> Result where E: Error, { - self.0.get_with_name(type_name).ok_or_else(|| { - Error::custom(format_args!("No registration found for `{type_name}`")) + self.0.get_with_type_path(type_path).ok_or_else(|| { + Error::custom(format_args!("No registration found for `{type_path}`")) }) } } @@ -377,7 +377,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { where D: serde::Deserializer<'de>, { - let type_name = self.registration.type_name(); + let type_path = self.registration.type_info().type_path(); // Handle both Value case and types that have a custom `ReflectDeserialize` if let Some(deserialize_reflect) = self.registration.data::() { @@ -388,7 +388,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { match self.registration.type_info() { TypeInfo::Struct(struct_info) => { let mut dynamic_struct = deserializer.deserialize_struct( - struct_info.name(), + struct_info.type_path_table().ident().unwrap(), struct_info.field_names(), StructVisitor { struct_info, @@ -401,7 +401,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { } TypeInfo::TupleStruct(tuple_struct_info) => { let mut dynamic_tuple_struct = deserializer.deserialize_tuple_struct( - tuple_struct_info.name(), + tuple_struct_info.type_path_table().ident().unwrap(), tuple_struct_info.field_len(), TupleStructVisitor { tuple_struct_info, @@ -451,15 +451,17 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { Ok(Box::new(dynamic_tuple)) } TypeInfo::Enum(enum_info) => { - let type_name = enum_info.type_name(); - let mut dynamic_enum = if type_name.starts_with("core::option::Option") { + let mut dynamic_enum = if enum_info.type_path_table().module_path() + == Some("core::option") + && enum_info.type_path_table().ident() == Some("Option") + { deserializer.deserialize_option(OptionVisitor { enum_info, registry: self.registry, })? } else { deserializer.deserialize_enum( - enum_info.name(), + enum_info.type_path_table().ident().unwrap(), enum_info.variant_names(), EnumVisitor { enum_info, @@ -474,7 +476,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { TypeInfo::Value(_) => { // This case should already be handled Err(de::Error::custom(format_args!( - "the TypeRegistration for {type_name} doesn't have ReflectDeserialize", + "the TypeRegistration for {type_path} doesn't have ReflectDeserialize", ))) } } @@ -578,10 +580,10 @@ impl<'a, 'de> Visitor<'de> for TupleStructVisitor<'a> { de::Error::custom(format_args!( "no field at index {} on tuple {}", index, - self.tuple_struct_info.type_name(), + self.tuple_struct_info.type_path(), )) })?; - get_registration(field.type_id(), field.type_name(), self.registry) + get_registration(field.type_id(), field.type_path(), self.registry) }; while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { @@ -650,7 +652,7 @@ impl<'a, 'de> Visitor<'de> for ArrayVisitor<'a> { let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or_default()); let registration = get_registration( self.array_info.item_type_id(), - self.array_info.item_type_name(), + self.array_info.item_type_path_table().path(), self.registry, )?; while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { @@ -690,7 +692,7 @@ impl<'a, 'de> Visitor<'de> for ListVisitor<'a> { let mut list = DynamicList::default(); let registration = get_registration( self.list_info.item_type_id(), - self.list_info.item_type_name(), + self.list_info.item_type_path_table().path(), self.registry, )?; while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { @@ -722,12 +724,12 @@ impl<'a, 'de> Visitor<'de> for MapVisitor<'a> { let mut dynamic_map = DynamicMap::default(); let key_registration = get_registration( self.map_info.key_type_id(), - self.map_info.key_type_name(), + self.map_info.key_type_path_table().path(), self.registry, )?; let value_registration = get_registration( self.map_info.value_type_id(), - self.map_info.value_type_name(), + self.map_info.value_type_path_table().path(), self.registry, )?; while let Some(key) = map.next_key_seed(TypedReflectDeserializer { @@ -782,7 +784,7 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { VariantInfo::Tuple(tuple_info) if tuple_info.field_len() == 1 => { let field = tuple_info.field_at(0).unwrap(); let registration = - get_registration(field.type_id(), field.type_name(), self.registry)?; + get_registration(field.type_id(), field.type_path(), self.registry)?; let value = variant.newtype_variant_seed(TypedReflectDeserializer { registration, registry: self.registry, @@ -850,7 +852,7 @@ impl<'de> DeserializeSeed<'de> for VariantDeserializer { Error::custom(format_args!( "no variant found at index `{}` on enum `{}`", variant_index, - self.0.name() + self.0.type_path() )) }) } @@ -960,7 +962,7 @@ impl<'a, 'de> Visitor<'de> for OptionVisitor<'a> { fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { formatter.write_str("reflected option value of type ")?; - formatter.write_str(self.enum_info.type_name()) + formatter.write_str(self.enum_info.type_path()) } fn visit_some(self, deserializer: D) -> Result @@ -972,7 +974,7 @@ impl<'a, 'de> Visitor<'de> for OptionVisitor<'a> { VariantInfo::Tuple(tuple_info) if tuple_info.field_len() == 1 => { let field = tuple_info.field_at(0).unwrap(); let registration = - get_registration(field.type_id(), field.type_name(), self.registry)?; + get_registration(field.type_id(), field.type_path(), self.registry)?; let de = TypedReflectDeserializer { registration, registry: self.registry, @@ -1019,7 +1021,7 @@ where ExpectedValues(fields.collect()) )) })?; - let registration = get_registration(field.type_id(), field.type_name(), registry)?; + let registration = get_registration(field.type_id(), field.type_path(), registry)?; let value = map.next_value_seed(TypedReflectDeserializer { registration, registry, @@ -1046,7 +1048,7 @@ where let field = info.get_field(index).ok_or_else(|| { Error::invalid_length(index, &info.get_field_len().to_string().as_str()) })?; - get_registration(field.type_id(), field.type_name(), registry) + get_registration(field.type_id(), field.type_path(), registry) }; while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { @@ -1074,11 +1076,11 @@ where fn get_registration<'a, E: Error>( type_id: TypeId, - type_name: &str, + type_path: &str, registry: &'a TypeRegistry, ) -> Result<&'a TypeRegistration, E> { let registration = registry.get(type_id).ok_or_else(|| { - Error::custom(format_args!("no registration found for type `{type_name}`")) + Error::custom(format_args!("no registration found for type `{type_path}`")) })?; Ok(registration) } @@ -1356,7 +1358,7 @@ mod tests { // === Normal === // let input = r#"{ - "bevy_reflect::serde::de::tests::should_deserialize_option::OptionTest": ( + "bevy_reflect::serde::de::tests::OptionTest": ( none: None, simple: Some("Hello world!"), complex: Some(( @@ -1378,7 +1380,7 @@ mod tests { let input = r#" #![enable(implicit_some)] { - "bevy_reflect::serde::de::tests::should_deserialize_option::OptionTest": ( + "bevy_reflect::serde::de::tests::OptionTest": ( none: None, simple: "Hello world!", complex: ( @@ -1415,7 +1417,7 @@ mod tests { // === Unit Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": Unit, + "bevy_reflect::serde::de::tests::MyEnum": Unit, }"#; let reflect_deserializer = UntypedReflectDeserializer::new(®istry); let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); @@ -1426,7 +1428,7 @@ mod tests { // === NewType Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": NewType(123), + "bevy_reflect::serde::de::tests::MyEnum": NewType(123), }"#; let reflect_deserializer = UntypedReflectDeserializer::new(®istry); let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); @@ -1437,7 +1439,7 @@ mod tests { // === Tuple Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": Tuple(1.23, 3.21), + "bevy_reflect::serde::de::tests::MyEnum": Tuple(1.23, 3.21), }"#; let reflect_deserializer = UntypedReflectDeserializer::new(®istry); let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); @@ -1448,7 +1450,7 @@ mod tests { // === Struct Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": Struct( + "bevy_reflect::serde::de::tests::MyEnum": Struct( value: "I <3 Enums", ), }"#; diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index 58231b2654ac2..b675b5dcb2395 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -94,7 +94,7 @@ mod tests { } #[test] - #[should_panic(expected = "cannot get type info for bevy_reflect::struct_trait::DynamicStruct")] + #[should_panic(expected = "cannot get type info for bevy_reflect::DynamicStruct")] fn unproxied_dynamic_should_not_serialize() { let registry = TypeRegistry::default(); diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index 033e1fb105f28..da0b464893dcf 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -37,7 +37,7 @@ fn get_serializable<'a, E: serde::ser::Error>( .ok_or_else(|| { serde::ser::Error::custom(format_args!( "Type '{}' did not register ReflectSerialize", - reflect_value.type_name() + reflect_value.reflect_type_path() )) })?; Ok(reflect_serialize.get_serializable(reflect_value)) @@ -46,10 +46,10 @@ fn get_serializable<'a, E: serde::ser::Error>( /// A general purpose serializer for reflected types. /// /// The serialized data will take the form of a map containing the following entries: -/// 1. `type`: The _full_ [type name] +/// 1. `type`: The _full_ [type path] /// 2. `value`: The serialized value of the reflected type /// -/// [type name]: std::any::type_name +/// [type path]: crate::TypePath::type_path pub struct ReflectSerializer<'a> { pub value: &'a dyn Reflect, pub registry: &'a TypeRegistry, @@ -68,7 +68,7 @@ impl<'a> Serialize for ReflectSerializer<'a> { { let mut state = serializer.serialize_map(Some(1))?; state.serialize_entry( - self.value.type_name(), + self.value.reflect_type_path(), &TypedReflectSerializer::new(self.value, self.registry), )?; state.end() @@ -172,7 +172,7 @@ impl<'a> Serialize for StructSerializer<'a> { .ok_or_else(|| { Error::custom(format_args!( "cannot get type info for {}", - self.struct_value.type_name() + self.struct_value.reflect_type_path() )) })?; @@ -191,7 +191,7 @@ impl<'a> Serialize for StructSerializer<'a> { .and_then(|registration| registration.data::()); let ignored_len = serialization_data.map(|data| data.len()).unwrap_or(0); let mut state = serializer.serialize_struct( - struct_info.name(), + struct_info.type_path_table().ident().unwrap(), self.struct_value.field_len() - ignored_len, )?; @@ -225,7 +225,7 @@ impl<'a> Serialize for TupleStructSerializer<'a> { .ok_or_else(|| { Error::custom(format_args!( "cannot get type info for {}", - self.tuple_struct.type_name() + self.tuple_struct.reflect_type_path() )) })?; @@ -244,7 +244,7 @@ impl<'a> Serialize for TupleStructSerializer<'a> { .and_then(|registration| registration.data::()); let ignored_len = serialization_data.map(|data| data.len()).unwrap_or(0); let mut state = serializer.serialize_tuple_struct( - tuple_struct_info.name(), + tuple_struct_info.type_path_table().ident().unwrap(), self.tuple_struct.field_len() - ignored_len, )?; @@ -274,7 +274,7 @@ impl<'a> Serialize for EnumSerializer<'a> { let type_info = self.enum_value.get_represented_type_info().ok_or_else(|| { Error::custom(format_args!( "cannot get type info for {}", - self.enum_value.type_name() + self.enum_value.reflect_type_path() )) })?; @@ -287,7 +287,7 @@ impl<'a> Serialize for EnumSerializer<'a> { } }; - let enum_name = enum_info.name(); + let enum_name = enum_info.type_path_table().ident().unwrap(); let variant_index = self.enum_value.variant_index() as u32; let variant_info = enum_info .variant_at(variant_index as usize) @@ -302,10 +302,8 @@ impl<'a> Serialize for EnumSerializer<'a> { match variant_type { VariantType::Unit => { - if self - .enum_value - .type_name() - .starts_with("core::option::Option") + if self.enum_value.reflect_module_path() == Some("core::option") + && self.enum_value.reflect_type_ident() == Some("Option") { serializer.serialize_none() } else { @@ -341,7 +339,7 @@ impl<'a> Serialize for EnumSerializer<'a> { let field = self.enum_value.field_at(0).unwrap(); if self .enum_value - .type_name() + .reflect_type_path() .starts_with("core::option::Option") { serializer.serialize_some(&TypedReflectSerializer::new(field, self.registry)) @@ -667,7 +665,7 @@ mod tests { let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::should_serialize_option::OptionTest": ( + "bevy_reflect::serde::ser::tests::OptionTest": ( none: None, simple: Some("Hello world!"), complex: Some(( @@ -687,7 +685,7 @@ mod tests { let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); let expected = r#"#![enable(implicit_some)] { - "bevy_reflect::serde::ser::tests::should_serialize_option::OptionTest": ( + "bevy_reflect::serde::ser::tests::OptionTest": ( none: None, simple: "Hello world!", complex: ( @@ -719,7 +717,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": Unit, + "bevy_reflect::serde::ser::tests::MyEnum": Unit, }"#; assert_eq!(expected, output); @@ -728,7 +726,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": NewType(123), + "bevy_reflect::serde::ser::tests::MyEnum": NewType(123), }"#; assert_eq!(expected, output); @@ -737,7 +735,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": Tuple(1.23, 3.21), + "bevy_reflect::serde::ser::tests::MyEnum": Tuple(1.23, 3.21), }"#; assert_eq!(expected, output); @@ -748,7 +746,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": Struct( + "bevy_reflect::serde::ser::tests::MyEnum": Struct( value: "I <3 Enums", ), }"#; diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index f2bb3a4563fd1..9ecf2784abb0c 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,5 +1,6 @@ use crate::{ self as bevy_reflect, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + TypePath, TypePathTable, }; use bevy_reflect_derive::impl_type_path; use bevy_utils::{Entry, HashMap}; @@ -75,8 +76,7 @@ pub trait Struct: Reflect { /// A container for compile-time named struct info. #[derive(Clone, Debug)] pub struct StructInfo { - name: &'static str, - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, fields: Box<[NamedField]>, field_names: Box<[&'static str]>, @@ -90,10 +90,9 @@ impl StructInfo { /// /// # Arguments /// - /// * `name`: The name of this struct (_without_ generics or lifetimes) /// * `fields`: The fields of this struct in the order they are defined /// - pub fn new(name: &'static str, fields: &[NamedField]) -> Self { + pub fn new(fields: &[NamedField]) -> Self { let field_indices = fields .iter() .enumerate() @@ -103,8 +102,7 @@ impl StructInfo { let field_names = fields.iter().map(|field| field.name()).collect(); Self { - name, - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), field_names, @@ -152,20 +150,21 @@ impl StructInfo { self.fields.len() } - /// The name of the struct. + /// A representation of the type path of the struct. /// - /// This does _not_ include any generics or lifetimes. - /// - /// For example, `foo::bar::Baz<'a, T>` would simply be `Baz`. - pub fn name(&self) -> &'static str { - self.name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path } - /// The [type name] of the struct. + /// The [stable, full type path] of the struct. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the struct. @@ -392,13 +391,6 @@ impl Struct for DynamicStruct { } impl Reflect for DynamicStruct { - #[inline] - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_else(|| std::any::type_name::()) - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type @@ -551,7 +543,7 @@ pub fn struct_partial_eq(a: &S, b: &dyn Reflect) -> Option { /// ``` #[inline] pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut debug = f.debug_struct(dyn_struct.type_name()); + let mut debug = f.debug_struct(dyn_struct.reflect_type_path()); for field_index in 0..dyn_struct.field_len() { let field = dyn_struct.field_at(field_index).unwrap(); debug.field( diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 0480d3b3094a3..87ad68c59f0ff 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -1,12 +1,13 @@ use bevy_reflect_derive::impl_type_path; +use bevy_utils::all_tuples; +use crate::TypePathTable; use crate::{ self as bevy_reflect, utility::GenericTypePathCell, FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypeRegistration, Typed, UnnamedField, }; use std::any::{Any, TypeId}; -use std::borrow::Cow; use std::fmt::{Debug, Formatter}; use std::slice::Iter; @@ -138,7 +139,7 @@ impl GetTupleField for dyn Tuple { /// A container for compile-time tuple info. #[derive(Clone, Debug)] pub struct TupleInfo { - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, fields: Box<[UnnamedField]>, #[cfg(feature = "documentation")] @@ -152,9 +153,9 @@ impl TupleInfo { /// /// * `fields`: The fields of this tuple in the order they are defined /// - pub fn new(fields: &[UnnamedField]) -> Self { + pub fn new(fields: &[UnnamedField]) -> Self { Self { - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), #[cfg(feature = "documentation")] @@ -183,11 +184,21 @@ impl TupleInfo { self.fields.len() } - /// The [type name] of the tuple. + /// A representation of the type path of the tuple. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the tuple. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the tuple. @@ -210,7 +221,6 @@ impl TupleInfo { /// A tuple which allows fields to be added at runtime. #[derive(Default, Debug)] pub struct DynamicTuple { - name: Cow<'static, str>, represented_type: Option<&'static TypeInfo>, fields: Vec>, } @@ -230,8 +240,6 @@ impl DynamicTuple { "expected TypeInfo::Tuple but received: {:?}", represented_type ); - - self.name = Cow::Borrowed(represented_type.type_name()); } self.represented_type = represented_type; } @@ -240,28 +248,12 @@ impl DynamicTuple { pub fn insert_boxed(&mut self, value: Box) { self.represented_type = None; self.fields.push(value); - self.generate_name(); } /// Appends a typed element with value `value` to the tuple. pub fn insert(&mut self, value: T) { self.represented_type = None; self.insert_boxed(Box::new(value)); - self.generate_name(); - } - - fn generate_name(&mut self) { - let mut name = self.name.to_string(); - name.clear(); - name.push('('); - for (i, field) in self.fields.iter().enumerate() { - if i > 0 { - name.push_str(", "); - } - name.push_str(field.type_name()); - } - name.push(')'); - self.name = Cow::Owned(name); } } @@ -297,7 +289,6 @@ impl Tuple for DynamicTuple { #[inline] fn clone_dynamic(&self) -> DynamicTuple { DynamicTuple { - name: self.name.clone(), represented_type: self.represented_type, fields: self .fields @@ -309,13 +300,6 @@ impl Tuple for DynamicTuple { } impl Reflect for DynamicTuple { - #[inline] - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_else(|| &self.name) - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type @@ -514,7 +498,6 @@ macro_rules! impl_reflect_tuple { fn clone_dynamic(&self) -> DynamicTuple { let info = self.get_represented_type_info(); DynamicTuple { - name: Cow::Borrowed(::core::any::type_name::()), represented_type: info, fields: self .iter_fields() @@ -525,10 +508,6 @@ macro_rules! impl_reflect_tuple { } impl<$($name: Reflect + TypePath),*> Reflect for ($($name,)*) { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -600,22 +579,6 @@ macro_rules! impl_reflect_tuple { } } - impl <$($name: Reflect + TypePath),*> TypePath for ($($name,)*) { - fn type_path() -> &'static str { - static CELL: GenericTypePathCell = GenericTypePathCell::new(); - CELL.get_or_insert::(|| { - "(".to_owned() $(+ <$name as TypePath>::type_path())* + ")" - }) - } - - fn short_type_path() -> &'static str { - static CELL: GenericTypePathCell = GenericTypePathCell::new(); - CELL.get_or_insert::(|| { - "(".to_owned() $(+ <$name as TypePath>::short_type_path())* + ")" - }) - } - } - impl<$($name: Reflect + TypePath),*> GetTypeRegistration for ($($name,)*) { fn get_type_registration() -> TypeRegistration { @@ -655,3 +618,56 @@ impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I} impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J} impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K} impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L} + +macro_rules! impl_type_path_tuple { + () => { + impl TypePath for () { + fn type_path() -> &'static str { + "()" + } + + fn short_type_path() -> &'static str { + "()" + } + } + }; + + ($param:ident) => { + impl <$param: TypePath> TypePath for ($param,) { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + "(".to_owned() + $param::type_path() + ",)" + }) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + "(".to_owned() + $param::short_type_path() + ",)" + }) + } + } + }; + + ($last:ident $(,$param:ident)*) => { + + impl <$($param: TypePath,)* $last: TypePath> TypePath for ($($param,)* $last) { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + "(".to_owned() $(+ $param::type_path() + ", ")* + $last::type_path() + ")" + }) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + "(".to_owned() $(+ $param::short_type_path() + ", ")* + $last::short_type_path() + ")" + }) + } + } + }; +} + +all_tuples!(impl_type_path_tuple, 0, 12, P); diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index ae455be39108c..9d12490871980 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,7 +1,8 @@ use bevy_reflect_derive::impl_type_path; use crate::{ - self as bevy_reflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, UnnamedField, + self as bevy_reflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, + TypePathTable, UnnamedField, }; use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; @@ -55,8 +56,7 @@ pub trait TupleStruct: Reflect { /// A container for compile-time tuple struct info. #[derive(Clone, Debug)] pub struct TupleStructInfo { - name: &'static str, - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, fields: Box<[UnnamedField]>, #[cfg(feature = "documentation")] @@ -68,13 +68,11 @@ impl TupleStructInfo { /// /// # Arguments /// - /// * `name`: The name of this struct (_without_ generics or lifetimes) /// * `fields`: The fields of this struct in the order they are defined /// - pub fn new(name: &'static str, fields: &[UnnamedField]) -> Self { + pub fn new(fields: &[UnnamedField]) -> Self { Self { - name, - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), #[cfg(feature = "documentation")] @@ -103,20 +101,21 @@ impl TupleStructInfo { self.fields.len() } - /// The name of the struct. + /// A representation of the type path of the struct. /// - /// This does _not_ include any generics or lifetimes. - /// - /// For example, `foo::bar::Baz<'a, T>` would simply be `Baz`. - pub fn name(&self) -> &'static str { - self.name + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path } - /// The [type name] of the tuple struct. + /// The [stable, full type path] of the struct. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the tuple struct. @@ -295,13 +294,6 @@ impl TupleStruct for DynamicTupleStruct { } impl Reflect for DynamicTupleStruct { - #[inline] - fn type_name(&self) -> &str { - self.represented_type - .map(|info| info.type_name()) - .unwrap_or_else(|| std::any::type_name::()) - } - #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type @@ -452,7 +444,7 @@ pub fn tuple_struct_debug( dyn_tuple_struct: &dyn TupleStruct, f: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - let mut debug = f.debug_tuple(dyn_tuple_struct.type_name()); + let mut debug = f.debug_tuple(dyn_tuple_struct.reflect_type_path()); for field in dyn_tuple_struct.iter_fields() { debug.field(&field as &dyn Debug); } diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 6db1f26b49992..8d677c908c9a8 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -1,5 +1,6 @@ use crate::{ ArrayInfo, EnumInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, TupleStructInfo, + TypePath, TypePathTable, }; use std::any::{Any, TypeId}; use std::fmt::Debug; @@ -41,15 +42,17 @@ use std::fmt::Debug; /// NamedField::new::("foo"), /// NamedField::new::<(f32, f32) >("bar"), /// ]; -/// let info = StructInfo::new::("MyStruct", &fields); +/// let info = StructInfo::new::(&fields); /// TypeInfo::Struct(info) /// }) /// } /// } /// -/// # +/// # impl TypePath for MyStruct { +/// # fn type_path() -> &'static str { todo!() } +/// # fn short_type_path() -> &'static str { todo!() } +/// # } /// # impl Reflect for MyStruct { -/// # fn type_name(&self) -> &str { todo!() } /// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } @@ -64,15 +67,10 @@ use std::fmt::Debug; /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } /// # fn clone_value(&self) -> Box { todo!() } /// # } -/// # -/// # impl TypePath for MyStruct { -/// # fn type_path() -> &'static str { todo!() } -/// # fn short_type_path() -> &'static str { todo!() } -/// # } /// ``` /// /// [utility]: crate::utility -pub trait Typed: Reflect { +pub trait Typed: Reflect + TypePath { /// Returns the compile-time [info] for the underlying type. /// /// [info]: TypeInfo @@ -90,7 +88,7 @@ pub trait Typed: Reflect { /// Each return a static reference to [`TypeInfo`], but they all have their own use cases. /// For example, if you know the type at compile time, [`Typed::type_info`] is probably /// the simplest. If all you have is a `dyn Reflect`, you'll probably want [`Reflect::get_represented_type_info`]. -/// Lastly, if all you have is a [`TypeId`] or [type name], you will need to go through +/// Lastly, if all you have is a [`TypeId`] or [type path], you will need to go through /// [`TypeRegistry::get_type_info`]. /// /// You may also opt to use [`TypeRegistry::get_type_info`] in place of the other methods simply because @@ -100,7 +98,7 @@ pub trait Typed: Reflect { /// [`Reflect::get_represented_type_info`]: crate::Reflect::get_represented_type_info /// [`TypeRegistry::get_type_info`]: crate::TypeRegistry::get_type_info /// [`TypeId`]: std::any::TypeId -/// [type name]: std::any::type_name +/// [type path]: TypePath::type_path #[derive(Debug, Clone)] pub enum TypeInfo { Struct(StructInfo), @@ -128,22 +126,32 @@ impl TypeInfo { } } - /// The [name] of the underlying type. + /// A representation of the type path of the underlying type. /// - /// [name]: std::any::type_name - pub fn type_name(&self) -> &'static str { + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { match self { - Self::Struct(info) => info.type_name(), - Self::TupleStruct(info) => info.type_name(), - Self::Tuple(info) => info.type_name(), - Self::List(info) => info.type_name(), - Self::Array(info) => info.type_name(), - Self::Map(info) => info.type_name(), - Self::Enum(info) => info.type_name(), - Self::Value(info) => info.type_name(), + Self::Struct(info) => info.type_path_table(), + Self::TupleStruct(info) => info.type_path_table(), + Self::Tuple(info) => info.type_path_table(), + Self::List(info) => info.type_path_table(), + Self::Array(info) => info.type_path_table(), + Self::Map(info) => info.type_path_table(), + Self::Enum(info) => info.type_path_table(), + Self::Value(info) => info.type_path_table(), } } + /// The [stable, full type path] of the underlying type. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. + /// + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() + } + /// Check if the given type matches the underlying type. pub fn is(&self) -> bool { TypeId::of::() == self.type_id() @@ -175,16 +183,16 @@ impl TypeInfo { /// it _as_ a struct. It therefore makes more sense to represent it as a [`ValueInfo`]. #[derive(Debug, Clone)] pub struct ValueInfo { - type_name: &'static str, + type_path: TypePathTable, type_id: TypeId, #[cfg(feature = "documentation")] docs: Option<&'static str>, } impl ValueInfo { - pub fn new() -> Self { + pub fn new() -> Self { Self { - type_name: std::any::type_name::(), + type_path: TypePathTable::of::(), type_id: TypeId::of::(), #[cfg(feature = "documentation")] docs: None, @@ -197,11 +205,21 @@ impl ValueInfo { Self { docs: doc, ..self } } - /// The [type name] of the value. + /// A representation of the type path of the value. + /// + /// Provides dynamic access to all methods on [`TypePath`]. + pub fn type_path_table(&self) -> &TypePathTable { + &self.type_path + } + + /// The [stable, full type path] of the value. + /// + /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`]. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [stable, full type path]: TypePath + /// [`type_path_table`]: Self::type_path_table + pub fn type_path(&self) -> &'static str { + self.type_path_table().path() } /// The [`TypeId`] of the value. diff --git a/crates/bevy_reflect/src/type_path.rs b/crates/bevy_reflect/src/type_path.rs index 68c2e64690567..9861f76289b66 100644 --- a/crates/bevy_reflect/src/type_path.rs +++ b/crates/bevy_reflect/src/type_path.rs @@ -1,3 +1,5 @@ +use std::fmt; + /// A static accessor to type paths and names. /// /// The engine uses this trait over [`std::any::type_name`] for stability and flexibility. @@ -82,21 +84,21 @@ pub trait TypePath: 'static { /// /// Generic parameter types are also fully expanded. /// - /// For `Option`, this is `"core::option::Option"`. + /// For `Option>`, this is `"core::option::Option>"`. fn type_path() -> &'static str; /// Returns a short, pretty-print enabled path to the type. /// /// Generic parameter types are also shortened. /// - /// For `Option`, this is `"Option"`. + /// For `Option>`, this is `"Option>"`. fn short_type_path() -> &'static str; /// Returns the name of the type, or [`None`] if it is [anonymous]. /// /// Primitive types will return [`Some`]. /// - /// For `Option`, this is `"Option"`. + /// For `Option>`, this is `"Option"`. /// /// [anonymous]: TypePath#anonymity fn type_ident() -> Option<&'static str> { @@ -105,7 +107,7 @@ pub trait TypePath: 'static { /// Returns the name of the crate the type is in, or [`None`] if it is [anonymous]. /// - /// For `Option`, this is `"core"`. + /// For `Option>`, this is `"core"`. /// /// [anonymous]: TypePath#anonymity fn crate_name() -> Option<&'static str> { @@ -114,7 +116,7 @@ pub trait TypePath: 'static { /// Returns the path to the module the type is in, or [`None`] if it is [anonymous]. /// - /// For `Option`, this is `"core::option"`. + /// For `Option>`, this is `"core::option"`. /// /// [anonymous]: TypePath#anonymity fn module_path() -> Option<&'static str> { @@ -123,6 +125,10 @@ pub trait TypePath: 'static { } /// Dynamic dispatch for [`TypePath`]. +/// +/// Since this is a supertrait of [`Reflect`] its methods can be called on a `dyn Reflect`. +/// +/// [`Reflect`]: crate::Reflect pub trait DynamicTypePath { /// See [`TypePath::type_path`]. fn reflect_type_path(&self) -> &str; @@ -161,3 +167,60 @@ impl DynamicTypePath for T { Self::module_path() } } + +/// Provides dynamic access to all methods on [`TypePath`]. +#[derive(Clone, Copy)] +pub struct TypePathTable { + // Cache the type path as it is likely the only one that will be used. + type_path: &'static str, + short_type_path: fn() -> &'static str, + type_ident: fn() -> Option<&'static str>, + crate_name: fn() -> Option<&'static str>, + module_path: fn() -> Option<&'static str>, +} + +impl fmt::Debug for TypePathTable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TypePathVtable") + .field("type_path", &self.type_path) + .finish() + } +} + +impl TypePathTable { + /// Creates a new table from a type. + pub fn of() -> Self { + Self { + type_path: T::type_path(), + short_type_path: T::short_type_path, + type_ident: T::type_ident, + crate_name: T::crate_name, + module_path: T::module_path, + } + } + + /// See [`TypePath::type_path`]. + pub fn path(&self) -> &'static str { + self.type_path + } + + /// See [`TypePath::short_type_path`]. + pub fn short_path(&self) -> &'static str { + (self.short_type_path)() + } + + /// See [`TypePath::type_ident`]. + pub fn ident(&self) -> Option<&'static str> { + (self.type_ident)() + } + + /// See [`TypePath::crate_name`]. + pub fn crate_name(&self) -> Option<&'static str> { + (self.crate_name)() + } + + /// See [`TypePath::module_path`]. + pub fn module_path(&self) -> Option<&'static str> { + (self.module_path)() + } +} diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 4e7ff1d398f5c..00843daebb8f4 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,4 +1,4 @@ -use crate::{serde::Serializable, Reflect, TypeInfo, Typed}; +use crate::{serde::Serializable, Reflect, TypeInfo, TypePath, Typed}; use bevy_ptr::{Ptr, PtrMut}; use bevy_utils::{HashMap, HashSet}; use downcast_rs::{impl_downcast, Downcast}; @@ -23,9 +23,9 @@ use std::{ /// [crate-level documentation]: crate pub struct TypeRegistry { registrations: HashMap, - short_name_to_id: HashMap, - full_name_to_id: HashMap, - ambiguous_names: HashSet, + short_path_to_id: HashMap<&'static str, TypeId>, + type_path_to_id: HashMap<&'static str, TypeId>, + ambiguous_names: HashSet<&'static str>, } // TODO: remove this wrapper once we migrate to Atelier Assets and the Scene AssetLoader doesn't @@ -41,7 +41,7 @@ impl Debug for TypeRegistryArc { self.internal .read() .unwrap_or_else(PoisonError::into_inner) - .full_name_to_id + .type_path_to_id .keys() .fmt(f) } @@ -71,8 +71,8 @@ impl TypeRegistry { pub fn empty() -> Self { Self { registrations: Default::default(), - short_name_to_id: Default::default(), - full_name_to_id: Default::default(), + short_path_to_id: Default::default(), + type_path_to_id: Default::default(), ambiguous_names: Default::default(), } } @@ -118,19 +118,19 @@ impl TypeRegistry { return; } - let short_name = registration.short_name.to_string(); - if self.short_name_to_id.contains_key(&short_name) - || self.ambiguous_names.contains(&short_name) + let short_name = registration.type_info().type_path_table().short_path(); + if self.short_path_to_id.contains_key(short_name) + || self.ambiguous_names.contains(short_name) { // name is ambiguous. fall back to long names for all ambiguous types - self.short_name_to_id.remove(&short_name); + self.short_path_to_id.remove(short_name); self.ambiguous_names.insert(short_name); } else { - self.short_name_to_id + self.short_path_to_id .insert(short_name, registration.type_id()); } - self.full_name_to_id - .insert(registration.type_name().to_string(), registration.type_id()); + self.type_path_to_id + .insert(registration.type_info().type_path(), registration.type_id()); self.registrations .insert(registration.type_id(), registration); } @@ -151,11 +151,11 @@ impl TypeRegistry { /// type_registry.register_type_data::, ReflectSerialize>(); /// type_registry.register_type_data::, ReflectDeserialize>(); /// ``` - pub fn register_type_data>(&mut self) { + pub fn register_type_data>(&mut self) { let data = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( "attempted to call `TypeRegistry::register_type_data` for type `{T}` with data `{D}` without registering `{T}` first", - T = std::any::type_name::(), + T = T::type_path(), D = std::any::type_name::(), ) }); @@ -183,48 +183,56 @@ impl TypeRegistry { } /// Returns a reference to the [`TypeRegistration`] of the type with the - /// given name. + /// given [type path]. /// - /// If no type with the given name has been registered, returns `None`. - pub fn get_with_name(&self, type_name: &str) -> Option<&TypeRegistration> { - self.full_name_to_id - .get(type_name) + /// If no type with the given path has been registered, returns `None`. + /// + /// [type path]: TypePath::type_path + pub fn get_with_type_path(&self, type_path: &str) -> Option<&TypeRegistration> { + self.type_path_to_id + .get(type_path) .and_then(|id| self.get(*id)) } /// Returns a mutable reference to the [`TypeRegistration`] of the type with - /// the given name. + /// the given [type path]. + /// + /// If no type with the given type path has been registered, returns `None`. /// - /// If no type with the given name has been registered, returns `None`. - pub fn get_with_name_mut(&mut self, type_name: &str) -> Option<&mut TypeRegistration> { - self.full_name_to_id - .get(type_name) + /// [type path]: TypePath::type_path + pub fn get_with_type_path_mut(&mut self, type_path: &str) -> Option<&mut TypeRegistration> { + self.type_path_to_id + .get(type_path) .cloned() .and_then(move |id| self.get_mut(id)) } /// Returns a reference to the [`TypeRegistration`] of the type with - /// the given short name. + /// the given [short type path]. /// - /// If the short name is ambiguous, or if no type with the given short name + /// If the short type path is ambiguous, or if no type with the given path /// has been registered, returns `None`. - pub fn get_with_short_name(&self, short_type_name: &str) -> Option<&TypeRegistration> { - self.short_name_to_id - .get(short_type_name) + /// + /// [type path]: TypePath::short_type_path + pub fn get_with_short_type_path(&self, short_type_path: &str) -> Option<&TypeRegistration> { + self.short_path_to_id + .get(short_type_path) .and_then(|id| self.registrations.get(id)) } /// Returns a mutable reference to the [`TypeRegistration`] of the type with - /// the given short name. + /// the given [short type path]. /// - /// If the short name is ambiguous, or if no type with the given short name + /// If the short type path is ambiguous, or if no type with the given path /// has been registered, returns `None`. - pub fn get_with_short_name_mut( + /// + /// [type path]: TypePath::short_type_path + pub fn get_with_short_type_path_mut( &mut self, - short_type_name: &str, + short_type_path: &str, ) -> Option<&mut TypeRegistration> { - self.short_name_to_id - .get(short_type_name) + self.short_path_to_id + .get(short_type_path) .and_then(|id| self.registrations.get_mut(id)) } @@ -292,7 +300,7 @@ impl TypeRegistryArc { /// but is more often automatically generated using [`#[derive(Reflect)]`](derive@crate::Reflect) which itself generates /// an implementation of the [`GetTypeRegistration`] trait. /// -/// Along with the type's [`TypeInfo`] and [short name], +/// Along with the type's [`TypeInfo`], /// this struct also contains a type's registered [`TypeData`]. /// /// See the [crate-level documentation] for more information on type registration. @@ -303,17 +311,15 @@ impl TypeRegistryArc { /// # use bevy_reflect::{TypeRegistration, std_traits::ReflectDefault, FromType}; /// let mut registration = TypeRegistration::of::>(); /// -/// assert_eq!("core::option::Option", registration.type_name()); -/// assert_eq!("Option", registration.short_name()); +/// assert_eq!("core::option::Option", registration.type_info().type_path()); +/// assert_eq!("Option", registration.type_info().type_path_table().short_path()); /// /// registration.insert::(FromType::>::from_type()); /// assert!(registration.data::().is_some()) /// ``` /// -/// [short name]: bevy_utils::get_short_name /// [crate-level documentation]: crate pub struct TypeRegistration { - short_name: String, data: HashMap>, type_info: &'static TypeInfo, } @@ -321,7 +327,6 @@ pub struct TypeRegistration { impl Debug for TypeRegistration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TypeRegistration") - .field("short_name", &self.short_name) .field("type_info", &self.type_info) .finish() } @@ -369,28 +374,12 @@ impl TypeRegistration { } /// Creates type registration information for `T`. - pub fn of() -> Self { - let type_name = std::any::type_name::(); + pub fn of() -> Self { Self { data: HashMap::default(), - short_name: bevy_utils::get_short_name(type_name), type_info: T::type_info(), } } - - /// Returns the [short name] of the type. - /// - /// [short name]: bevy_utils::get_short_name - pub fn short_name(&self) -> &str { - &self.short_name - } - - /// Returns the [name] of the type. - /// - /// [name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_info.type_name() - } } impl Clone for TypeRegistration { @@ -402,7 +391,6 @@ impl Clone for TypeRegistration { TypeRegistration { data, - short_name: self.short_name.clone(), type_info: self.type_info, } } @@ -455,7 +443,7 @@ impl FromType for ReflectSerialize { ReflectSerialize { get_serializable: |value| { let value = value.downcast_ref::().unwrap_or_else(|| { - panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.type_name(), std::any::type_name::()) + panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.reflect_type_path(), std::any::type_name::()) }); Serializable::Borrowed(value) }, @@ -611,9 +599,8 @@ impl FromType for ReflectFromPtr { #[cfg(test)] mod test { - use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration}; + use crate::{GetTypeRegistration, ReflectFromPtr}; use bevy_ptr::{Ptr, PtrMut}; - use bevy_utils::HashMap; use crate as bevy_reflect; use crate::Reflect; @@ -658,37 +645,4 @@ mod test { } } } - - #[test] - fn test_property_type_registration() { - assert_eq!( - TypeRegistration::of::>().short_name, - "Option" - ); - assert_eq!( - TypeRegistration::of::>().short_name, - "HashMap" - ); - assert_eq!( - TypeRegistration::of::>>().short_name, - "Option>" - ); - assert_eq!( - TypeRegistration::of::>>>().short_name, - "Option>>" - ); - assert_eq!( - TypeRegistration::of::>>>().short_name, - "Option>>" - ); - assert_eq!( - TypeRegistration::of::, Option>>>().short_name, - "Option, Option>>" - ); - assert_eq!( - TypeRegistration::of::, (String, Option)>>>() - .short_name, - "Option, (String, Option)>>" - ); - } } diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index d79410b140946..71f82539f114a 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -62,14 +62,16 @@ mod sealed { /// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); /// CELL.get_or_set(|| { /// let fields = [NamedField::new::("bar")]; -/// let info = StructInfo::new::("Foo", &fields); +/// let info = StructInfo::new::(&fields); /// TypeInfo::Struct(info) /// }) /// } /// } -/// # +/// # impl TypePath for Foo { +/// # fn type_path() -> &'static str { todo!() } +/// # fn short_type_path() -> &'static str { todo!() } +/// # } /// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } /// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } @@ -84,11 +86,6 @@ mod sealed { /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } /// # fn clone_value(&self) -> Box { todo!() } /// # } - -/// # impl TypePath for Foo { -/// # fn type_path() -> &'static str { todo!() } -/// # fn short_type_path() -> &'static str { todo!() } -/// # } /// ``` /// /// [`TypePath`]: crate::TypePath @@ -133,19 +130,21 @@ impl NonGenericTypeCell { /// /// struct Foo(T); /// -/// impl Typed for Foo { +/// impl Typed for Foo { /// fn type_info() -> &'static TypeInfo { /// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); /// CELL.get_or_insert::(|| { /// let fields = [UnnamedField::new::(0)]; -/// let info = TupleStructInfo::new::("Foo", &fields); +/// let info = TupleStructInfo::new::(&fields); /// TypeInfo::TupleStruct(info) /// }) /// } /// } -/// # -/// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } +/// # impl TypePath for Foo { +/// # fn type_path() -> &'static str { todo!() } +/// # fn short_type_path() -> &'static str { todo!() } +/// # } +/// # impl Reflect for Foo { /// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } @@ -160,22 +159,18 @@ impl NonGenericTypeCell { /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } /// # fn clone_value(&self) -> Box { todo!() } /// # } -/// # impl TypePath for Foo { -/// # fn type_path() -> &'static str { todo!() } -/// # fn short_type_path() -> &'static str { todo!() } -/// # } /// ``` /// /// Implementing [`TypePath`] with generics. /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath}; +/// # use bevy_reflect::TypePath; /// use bevy_reflect::utility::GenericTypePathCell; /// /// struct Foo(T); /// -/// impl TypePath for Foo { +/// impl TypePath for Foo { /// fn type_path() -> &'static str { /// static CELL: GenericTypePathCell = GenericTypePathCell::new(); /// CELL.get_or_insert::(|| format!("my_crate::foo::Foo<{}>", T::type_path())) @@ -185,24 +180,19 @@ impl NonGenericTypeCell { /// static CELL: GenericTypePathCell = GenericTypePathCell::new(); /// CELL.get_or_insert::(|| format!("Foo<{}>", T::short_type_path())) /// } +/// +/// fn type_ident() -> Option<&'static str> { +/// Some("Foo") +/// } +/// +/// fn module_path() -> Option<&'static str> { +/// Some("my_crate::foo") +/// } +/// +/// fn crate_name() -> Option<&'static str> { +/// Some("my_crate") +/// } /// } -/// # -/// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } -/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } -/// # fn into_any(self: Box) -> Box { todo!() } -/// # fn as_any(&self) -> &dyn Any { todo!() } -/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } -/// # fn into_reflect(self: Box) -> Box { todo!() } -/// # fn as_reflect(&self) -> &dyn Reflect { todo!() } -/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } -/// # fn apply(&mut self, value: &dyn Reflect) { todo!() } -/// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } -/// # fn reflect_ref(&self) -> ReflectRef { todo!() } -/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } -/// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } -/// # fn clone_value(&self) -> Box { todo!() } -/// # } /// ``` /// [`impl_type_path`]: crate::impl_type_path /// [`TypePath`]: crate::TypePath diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index dee1e4ae0f320..85055e218a3e7 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -73,14 +73,19 @@ impl DynamicScene { let type_registry = type_registry.read(); for resource in &self.resources { - let registration = type_registry - .get_with_name(resource.type_name()) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: resource.type_name().to_string(), - })?; + let type_info = resource.get_represented_type_info().ok_or_else(|| { + SceneSpawnError::NoRepresentedType { + type_path: resource.reflect_type_path().to_string(), + } + })?; + let registration = type_registry.get(type_info.type_id()).ok_or_else(|| { + SceneSpawnError::UnregisteredButReflectedType { + type_path: type_info.type_path().to_string(), + } + })?; let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { - type_name: resource.type_name().to_string(), + type_path: type_info.type_path().to_string(), } })?; @@ -106,15 +111,20 @@ impl DynamicScene { // Apply/ add each component to the given entity. for component in &scene_entity.components { - let registration = type_registry - .get_with_name(component.type_name()) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component.type_name().to_string(), - })?; + let type_info = component.get_represented_type_info().ok_or_else(|| { + SceneSpawnError::NoRepresentedType { + type_path: component.reflect_type_path().to_string(), + } + })?; + let registration = type_registry.get(type_info.type_id()).ok_or_else(|| { + SceneSpawnError::UnregisteredButReflectedType { + type_path: type_info.type_path().to_string(), + } + })?; let reflect_component = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredComponent { - type_name: component.type_name().to_string(), + type_path: type_info.type_path().to_string(), } })?; diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index afb178eb1943b..e6e9d4ddfb3f6 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -82,11 +82,11 @@ impl Scene { type_registry .get(type_id) .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component_info.name().to_string(), + std_type_name: component_info.name().to_string(), })?; let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { - type_name: component_info.name().to_string(), + type_path: registration.type_info().type_path().to_string(), } })?; reflect_resource.copy(&self.world, world); @@ -108,12 +108,12 @@ impl Scene { let reflect_component = type_registry .get(component_info.type_id().unwrap()) .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component_info.name().to_string(), + std_type_name: component_info.name().to_string(), }) .and_then(|registration| { registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredComponent { - type_name: component_info.name().to_string(), + type_path: registration.type_info().type_path().to_string(), } }) })?; diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index c0f1bb5781f4d..b45ffa91cdb8f 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -74,22 +74,42 @@ pub struct SceneSpawner { #[derive(Error, Debug)] pub enum SceneSpawnError { /// Scene contains an unregistered component type. - #[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")] + #[error("scene contains the unregistered component `{type_path}`. consider adding `#[reflect(Component)]` to your type")] UnregisteredComponent { /// Type of the unregistered component. - type_name: String, + type_path: String, }, /// Scene contains an unregistered resource type. - #[error("scene contains the unregistered resource `{type_name}`. consider adding `#[reflect(Resource)]` to your type")] + #[error("scene contains the unregistered resource `{type_path}`. consider adding `#[reflect(Resource)]` to your type")] UnregisteredResource { /// Type of the unregistered resource. - type_name: String, + type_path: String, }, /// Scene contains an unregistered type. - #[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::()`")] + #[error( + "scene contains the unregistered type `{std_type_name}`. \ + consider reflecting it with `#[derive(Reflect)]` \ + and registering the type using `app.register_type::()`" + )] UnregisteredType { + /// The [type name] for the unregistered type. + /// [type name]: std::any::type_name + std_type_name: String, + }, + /// Scene contains an unregistered type which has a `TypePath`. + #[error( + "scene contains the reflected type `{type_path}` but it was not found in the type registry. \ + consider registering the type using `app.register_type::()``" + )] + UnregisteredButReflectedType { /// The unregistered type. - type_name: String, + type_path: String, + }, + /// Scene contains a proxy without a represented type. + #[error("scene contains dynamic type `{type_path}` without a represented type. consider changing this using `set_represented_type`.")] + NoRepresentedType { + /// The dynamic instance type. + type_path: String, }, /// Dynamic scene with the given id does not exist. #[error("scene does not exist")] diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 515728cae00fa..e54513bd0a94d 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -181,7 +181,7 @@ impl<'a> Serialize for SceneMapSerializer<'a> { let mut state = serializer.serialize_map(Some(self.entries.len()))?; for reflect in self.entries { state.serialize_entry( - reflect.type_name(), + reflect.get_represented_type_info().unwrap().type_path(), &TypedReflectSerializer::new(&**reflect, &self.registry.read()), )?; } @@ -467,7 +467,7 @@ impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { if !added.insert(registration.type_id()) { return Err(Error::custom(format_args!( "duplicate reflect type: `{}`", - registration.type_name() + registration.type_info().type_path(), ))); } @@ -887,9 +887,15 @@ mod tests { let received = received .components .iter() - .find(|component| component.type_name() == expected.type_name()) + .find(|component| { + component.get_represented_type_info().unwrap().type_path() + == expected.get_represented_type_info().unwrap().type_path() + }) .unwrap_or_else(|| { - panic!("missing component (expected: `{}`)", expected.type_name()) + panic!( + "missing component (expected: `{}`)", + expected.get_represented_type_info().unwrap().type_path() + ) }); assert!( diff --git a/examples/reflection/generic_reflection.rs b/examples/reflection/generic_reflection.rs index 854bbb5ca18fd..409d814c327d0 100644 --- a/examples/reflection/generic_reflection.rs +++ b/examples/reflection/generic_reflection.rs @@ -21,7 +21,10 @@ fn setup(type_registry: Res) { let type_registry = type_registry.read(); let registration = type_registry.get(TypeId::of::>()).unwrap(); - info!("Registration for {} exists", registration.short_name()); + info!( + "Registration for {} exists", + registration.type_info().type_path(), + ); // MyType was not manually registered, so it does not exist assert!(type_registry.get(TypeId::of::>()).is_none());