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`](3fea3c6c0b/crates/bevy_reflect/src/impls/std.rs (L90))
(note that `String` is in the default Rust prelude) snuck in completely
under the radar.
This commit is contained in:
radiish 2023-10-09 20:33:03 +01:00 committed by GitHub
parent 92294de08d
commit 262846e702
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 816 additions and 787 deletions

View file

@ -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<T>,
>(
&mut self,

View file

@ -146,7 +146,8 @@ impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
.for_each(|field| insert_field::<B>(entity, field, registry)),
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
std::any::type_name::<B>()
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<B>(),
),
}
},
@ -163,7 +164,8 @@ impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
std::any::type_name::<B>()
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<B>(),
),
}
},
@ -188,14 +190,14 @@ fn insert_field<B: 'static>(
if world.components().get_id(TypeId::of::<B>()).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<B: 'static>(
if world.components().get_id(TypeId::of::<B>()).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(),
)
}
}

View file

@ -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<Cow<'static, str>>) -> &mut Self {
fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &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<dyn Reflect>,
) {
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::<ReflectComponent>() 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::<ReflectComponent>() 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,
&registry.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::<ComponentA>(), None);
@ -443,7 +443,7 @@ mod tests {
commands
.entity(entity)
.remove_reflect_with_registry::<TypeRegistryResource>(
boxed_reflect_component_a.type_name().to_owned(),
boxed_reflect_component_a.reflect_type_path().to_owned(),
);
system_state.apply(&mut world);

View file

@ -34,14 +34,14 @@ pub(crate) enum ReflectDerive<'a> {
/// // traits
/// // |----------------------------------------|
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// // type_name generics
/// // type_path generics
/// // |-------------------||----------|
/// struct ThingThatImReflecting<T1, T2, T3> {/* ... */}
/// ```
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
}

View file

@ -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::<Self>()),
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
}
} else {
#FQOption::None

View file

@ -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::<Self>(#string_name, &variants).with_docs(#doc)
#bevy_reflect_path::EnumInfo::new::<Self>(&variants).with_docs(#doc)
}
};
#[cfg(not(feature = "documentation"))]
let info_generator = {
quote! {
#bevy_reflect_path::EnumInfo::new::<Self>(#string_name, &variants)
#bevy_reflect_path::EnumInfo::new::<Self>(&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::<Self>()
}
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::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::<Self>()),
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::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));
}
}

View file

@ -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::<Self>(#string_name, &fields).with_docs(#doc)
#bevy_reflect_path::StructInfo::new::<Self>(&fields).with_docs(#doc)
}
};
#[cfg(not(feature = "documentation"))]
let info_generator = {
quote! {
#bevy_reflect_path::StructInfo::new::<Self>(#string_name, &fields)
#bevy_reflect_path::StructInfo::new::<Self>(&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::<Self>()
}
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())

View file

@ -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::<Self>(#string_name, &fields).with_docs(#doc)
#bevy_reflect_path::TupleStructInfo::new::<Self>(&fields).with_docs(#doc)
}
};
#[cfg(not(feature = "documentation"))]
let info_generator = {
quote! {
#bevy_reflect_path::TupleStructInfo::new::<Self>(#string_name, &fields)
#bevy_reflect_path::TupleStructInfo::new::<Self>(&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::<Self>()
}
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())

View file

@ -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::<Self>()
}
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
@ -96,7 +91,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
if let #FQOption::Some(value) = <dyn #FQAny>::downcast_ref::<Self>(value) {
*self = #FQClone::clone(value);
} else {
panic!("Value is not {}.", ::core::any::type_name::<Self>());
panic!("Value is not {}.", <Self as #bevy_reflect_path::TypePath>::type_path());
}
}

View file

@ -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<TArray: Array, TItem: Reflect>(capacity: usize) -> Self {
pub fn new<TArray: Array + TypePath, TItem: Reflect + TypePath>(capacity: usize) -> Self {
Self {
type_name: std::any::type_name::<TArray>(),
type_path: TypePathTable::of::<TArray>(),
type_id: TypeId::of::<TArray>(),
item_type_name: std::any::type_name::<TItem>(),
item_type_path: TypePathTable::of::<TItem>(),
item_type_id: TypeId::of::<TItem>(),
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::<T>() == 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::<Self>())
}
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
self.represented_type

View file

@ -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());
}
}

View file

@ -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<TEnum: Enum>(name: &'static str, variants: &[VariantInfo]) -> Self {
pub fn new<TEnum: Enum + TypePath>(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::<TEnum>(),
type_path: TypePathTable::of::<TEnum>(),
type_id: TypeId::of::<TEnum>(),
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.
///
/// [type name]: std::any::type_name
pub fn type_name(&self) -> &'static str {
self.type_name
/// 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 enum.

View file

@ -25,7 +25,15 @@ mod tests {
let info = MyEnum::type_info();
if let TypeInfo::Enum(info) = info {
assert!(info.is::<MyEnum>(), "expected type to be `MyEnum`");
assert_eq!(std::any::type_name::<MyEnum>(), 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();

View file

@ -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<T: Reflect>(name: &'static str) -> Self {
pub fn new<T: Reflect + TypePath>(name: &'static str) -> Self {
Self {
name,
type_name: std::any::type_name::<T>(),
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
#[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<T: Reflect>(index: usize) -> Self {
pub fn new<T: Reflect + TypePath>(index: usize) -> Self {
Self {
index,
type_name: std::any::type_name::<T>(),
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
#[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.
///
/// [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.

View file

@ -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(<Foo as Typed>::type_info()));
///
/// let registration = registry.get_with_name(reflected.type_name()).unwrap();
/// let registration = registry.get_with_type_path(<Foo as TypePath>::type_path()).unwrap();
/// let rfr = registration.data::<ReflectFromReflect>().unwrap();
///
/// let concrete: Box<dyn Reflect> = rfr.from_reflect(&reflected).unwrap();

View file

@ -11,7 +11,7 @@ use crate::{
impl<T: smallvec::Array + TypePath + Send + Sync> List for SmallVec<T>
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
<T as smallvec::Array>::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
<T as smallvec::Array>::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<T: smallvec::Array + TypePath + Send + Sync> Reflect for SmallVec<T>
where
T::Item: FromReflect,
T::Item: FromReflect + TypePath,
{
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -144,7 +140,7 @@ where
impl<T: smallvec::Array + TypePath + Send + Sync + 'static> Typed for SmallVec<T>
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<T: smallvec::Array + TypePath + Send + Sync>);
impl_type_path!(::smallvec::SmallVec<T: smallvec::Array>);
impl<T: smallvec::Array + TypePath + Send + Sync> FromReflect for SmallVec<T>
where
T::Item: FromReflect,
T::Item: FromReflect + TypePath,
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
@ -173,7 +169,7 @@ where
impl<T: smallvec::Array + TypePath + Send + Sync> GetTypeRegistration for SmallVec<T>
where
T::Item: FromReflect,
T::Item: FromReflect + TypePath,
{
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<SmallVec<T>>();

View file

@ -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<T: FromReflect + TypePath> Reflect for $ty {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -349,7 +346,7 @@ macro_rules! impl_reflect_for_veclike {
}
}
impl_type_path!($ty where T: FromReflect);
impl_type_path!($ty);
impl<T: FromReflect + TypePath> 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::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -607,23 +603,11 @@ macro_rules! impl_reflect_for_hashmap {
impl_reflect_for_hashmap!(::std::collections::HashMap<K, V, S>);
impl_type_path!(::std::collections::hash_map::RandomState);
impl_type_path!(
::std::collections::HashMap<K, V, S>
where
K: FromReflect + Eq + Hash + ?Sized,
V: FromReflect + ?Sized,
S: BuildHasher + Send + Sync + 'static,
);
impl_type_path!(::std::collections::HashMap<K, V, S>);
impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap<K, V, S>);
impl_type_path!(::bevy_utils::hashbrown::hash_map::DefaultHashBuilder);
impl_type_path!(
::bevy_utils::hashbrown::HashMap<K, V, S>
where
K: FromReflect + Eq + Hash + ?Sized,
V: FromReflect + ?Sized,
S: BuildHasher + Send + Sync + 'static,
);
impl_type_path!(::bevy_utils::hashbrown::HashMap<K, V, S>);
impl<T: Reflect + TypePath, const N: usize> Array for [T; N] {
#[inline]
@ -655,11 +639,6 @@ impl<T: Reflect + TypePath, const N: usize> Array for [T; N] {
}
impl<T: Reflect + TypePath, const N: usize> Reflect for [T; N] {
#[inline]
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -757,7 +736,7 @@ impl<T: Reflect + TypePath, const N: usize> Typed for [T; N] {
}
}
impl<T: Reflect + TypePath, const N: usize> TypePath for [T; N] {
impl<T: TypePath, const N: usize> TypePath for [T; N] {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("[{t}; {N}]", t = T::type_path()))
@ -871,11 +850,6 @@ impl<T: FromReflect + TypePath> Enum for Option<T> {
}
impl<T: FromReflect + TypePath> Reflect for Option<T> {
#[inline]
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
@ -929,7 +903,7 @@ impl<T: FromReflect + TypePath> Reflect for Option<T> {
.unwrap_or_else(|| {
panic!(
"Field in `Some` variant of {} should exist",
std::any::type_name::<Option<T>>()
Self::type_path()
)
})
.clone_value(),
@ -937,8 +911,8 @@ impl<T: FromReflect + TypePath> Reflect for Option<T> {
.unwrap_or_else(|_| {
panic!(
"Field in `Some` variant of {} should be of type {}",
std::any::type_name::<Option<T>>(),
std::any::type_name::<T>()
Self::type_path(),
T::type_path()
)
});
*self = Some(field);
@ -946,7 +920,7 @@ impl<T: FromReflect + TypePath> Reflect for Option<T> {
"None" => {
*self = None;
}
_ => panic!("Enum is not a {}.", std::any::type_name::<Self>()),
_ => panic!("Enum is not a {}.", Self::type_path()),
}
}
}
@ -995,7 +969,7 @@ impl<T: FromReflect + TypePath> FromReflect for Option<T> {
.unwrap_or_else(|| {
panic!(
"Field in `Some` variant of {} should exist",
std::any::type_name::<Option<T>>()
Option::<T>::type_path()
)
})
.clone_value(),
@ -1003,8 +977,8 @@ impl<T: FromReflect + TypePath> FromReflect for Option<T> {
.unwrap_or_else(|_| {
panic!(
"Field in `Some` variant of {} should be of type {}",
std::any::type_name::<Option<T>>(),
std::any::type_name::<T>()
Option::<T>::type_path(),
T::type_path()
)
});
Some(Some(field))
@ -1013,7 +987,7 @@ impl<T: FromReflect + TypePath> FromReflect for Option<T> {
name => panic!(
"variant with name `{}` does not exist on enum `{}`",
name,
std::any::type_name::<Self>()
Self::type_path()
),
}
} else {
@ -1029,21 +1003,38 @@ impl<T: FromReflect + TypePath> Typed for Option<T> {
let none_variant = VariantInfo::Unit(UnitVariantInfo::new("None"));
let some_variant =
VariantInfo::Tuple(TupleVariantInfo::new("Some", &[UnnamedField::new::<T>(0)]));
TypeInfo::Enum(EnumInfo::new::<Self>(
"Option",
&[none_variant, some_variant],
))
TypeInfo::Enum(EnumInfo::new::<Self>(&[none_variant, some_variant]))
})
}
}
impl_type_path!(::core::option::Option<T: FromReflect + TypePath>);
impl_type_path!(::core::option::Option<T>);
impl Reflect for Cow<'static, str> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
impl<T: TypePath + ?Sized> TypePath for &'static T {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("&{}", T::type_path()))
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("&{}", T::short_type_path()))
}
}
impl<T: TypePath + ?Sized> TypePath for &'static mut T {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("&mut {}", T::type_path()))
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("&mut {}", T::short_type_path()))
}
}
impl Reflect for Cow<'static, str> {
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -1077,7 +1068,7 @@ impl Reflect for Cow<'static, str> {
if let Some(value) = value.downcast_ref::<Self>() {
*self = value.clone();
} else {
panic!("Value is not a {}.", std::any::type_name::<Self>());
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::<Self, _>(|| "std::borrow::Cow::<str>".to_owned())
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "Cow<str>".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::<Cow<'static, str>>();
@ -1171,8 +1138,6 @@ impl FromReflect for Cow<'static, str> {
}
}
impl<T: PathOnly> PathOnly for [T] where [T]: ToOwned {}
impl<T: TypePath> TypePath for [T]
where
[T]: ToOwned,
@ -1188,8 +1153,6 @@ where
}
}
impl<T: ToOwned> PathOnly for T {}
impl<T: FromReflect + Clone + TypePath> 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<T: FromReflect + Clone + TypePath> 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<T: FromReflect + Clone + TypePath> 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<T: FromReflect + Clone + TypePath> List for Cow<'static, [T]> {
}
impl<T: FromReflect + Clone + TypePath> Reflect for Cow<'static, [T]> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -1344,10 +1303,6 @@ impl<T: FromReflect + Clone + TypePath> FromReflect for Cow<'static, [T]> {
}
impl Reflect for &'static Path {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -1381,7 +1336,7 @@ impl Reflect for &'static Path {
if let Some(&value) = value.downcast_ref::<Self>() {
*self = value;
} else {
panic!("Value is not a {}.", std::any::type_name::<Self>());
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::<Self, _>(|| "&std::path::Path".to_owned())
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "&Path".to_owned())
}
}
impl GetTypeRegistration for &'static Path {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
@ -1457,10 +1400,6 @@ impl FromReflect for &'static Path {
}
impl Reflect for Cow<'static, Path> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -1494,7 +1433,7 @@ impl Reflect for Cow<'static, Path> {
if let Some(value) = value.downcast_ref::<Self>() {
*self = value.clone();
} else {
panic!("Value is not a {}.", std::any::type_name::<Self>());
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<Self> {

View file

@ -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::<usize>::new();
let dyn_list = list.clone_dynamic();
assert_eq!(dyn_list.type_name(), std::any::type_name::<Vec<usize>>());
assert_ne!(dyn_list.reflect_type_path(), Vec::<usize>::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::<usize, String>::default();
let dyn_map = map.clone_dynamic();
assert_eq!(
dyn_map.type_name(),
std::any::type_name::<HashMap<usize, String>>()
assert_ne!(
dyn_map.reflect_type_path(),
HashMap::<usize, String>::type_path()
);
let tuple = (0usize, "1".to_string(), 2.0f32);
let mut dyn_tuple = tuple.clone_dynamic();
dyn_tuple.insert::<usize>(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::<TestStruct>());
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::<TestTupleStruct>()
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<T>(PhantomData<T>);
impl_type_path!((in my_alias as MyMacroNameG) MacroNameG<T>);
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_type_paths! {
Derive => "bevy_reflect::tests::Derive", "Derive",
DerivePath => "my_alias::DerivePath", "DerivePath",
DerivePathName => "my_alias::MyDerivePathName", "MyDerivePathName",
DeriveG<Param> => "bevy_reflect::tests::DeriveG<bevy_reflect::tests::Param>", "DeriveG<Param>",
DerivePathG<Param, 10> => "my_alias::DerivePathG<bevy_reflect::tests::Param, 10>", "DerivePathG<Param, 10>",
DerivePathNameG<Param> => "my_alias::MyDerivePathNameG<bevy_reflect::tests::Param>", "MyDerivePathNameG<Param>",
Macro => "my_alias::Macro", "Macro",
MacroName => "my_alias::MyMacroName", "MyMacroName",
MacroG<Param, 10> => "my_alias::MacroG<bevy_reflect::tests::Param, 10>", "MacroG<Param, 10>",
MacroNameG<Param> => "my_alias::MyMacroNameG<bevy_reflect::tests::Param>", "MyMacroNameG<Param>",
}
}
assert_eq!(
DeriveG::<Param>::type_path(),
"bevy_reflect::tests::DeriveG<bevy_reflect::tests::Param>"
);
assert_eq!(
DerivePathG::<Param, 10>::type_path(),
"my_alias::DerivePathG<bevy_reflect::tests::Param, 10>"
);
assert_eq!(
DerivePathNameG::<Param>::type_path(),
"my_alias::MyDerivePathNameG<bevy_reflect::tests::Param>"
);
#[test]
fn std_type_paths() {
#[derive(Clone)]
struct Type;
assert_eq!(Macro::type_path(), "my_alias::Macro");
assert_eq!(MacroName::type_path(), "my_alias::MyMacroName");
assert_eq!(
MacroG::<Param, 10>::type_path(),
"my_alias::MacroG<bevy_reflect::tests::Param, 10>"
);
assert_eq!(
MacroNameG::<Param>::type_path(),
"my_alias::MyMacroNameG<bevy_reflect::tests::Param>"
);
impl TypePath for Type {
fn type_path() -> &'static str {
// for brevity in tests
"Long"
}
assert_eq!(Derive::short_type_path(), "Derive");
assert_eq!(DerivePath::short_type_path(), "DerivePath");
assert_eq!(DerivePathName::short_type_path(), "MyDerivePathName");
fn short_type_path() -> &'static str {
"Short"
}
}
assert_eq!(DeriveG::<Param>::short_type_path(), "DeriveG<Param>");
assert_eq!(
DerivePathG::<Param, 10>::short_type_path(),
"DerivePathG<Param, 10>"
);
assert_eq!(
DerivePathNameG::<Param>::short_type_path(),
"MyDerivePathNameG<Param>"
);
assert_eq!(Macro::short_type_path(), "Macro");
assert_eq!(MacroName::short_type_path(), "MyMacroName");
assert_eq!(MacroG::<Param, 10>::short_type_path(), "MacroG<Param, 10>");
assert_eq!(
MacroNameG::<Param>::short_type_path(),
"MyMacroNameG<Param>"
);
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<Long>", "Cow<Short>",
}
}
#[test]
fn reflect_type_info() {
// TypeInfo
let info = i32::type_info();
assert_eq!(std::any::type_name::<i32>(), info.type_name());
assert_eq!(i32::type_path(), info.type_path());
assert_eq!(std::any::TypeId::of::<i32>(), 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::<MyStruct>());
assert_eq!(std::any::type_name::<MyStruct>(), info.type_name());
assert_eq!(
std::any::type_name::<i32>(),
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::<i32>(),
info.field("foo").unwrap().type_id()
);
assert!(info.field("foo").unwrap().is::<i32>());
assert_eq!("foo", info.field("foo").unwrap().name());
assert_eq!(
std::any::type_name::<usize>(),
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 = <MyGenericStruct<i32>>::type_info();
if let TypeInfo::Struct(info) = info {
assert!(info.is::<MyGenericStruct<i32>>());
assert_eq!(
std::any::type_name::<MyGenericStruct<i32>>(),
info.type_name()
);
assert_eq!(
std::any::type_name::<i32>(),
info.field("foo").unwrap().type_name()
);
assert_eq!(MyGenericStruct::<i32>::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::<usize>(),
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::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert_eq!(
std::any::type_name::<i32>(),
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::<i32>());
} 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::<MyTuple>());
assert_eq!(std::any::type_name::<MyTuple>(), info.type_name());
assert_eq!(
std::any::type_name::<f32>(),
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::<MyList>());
assert!(info.item_is::<usize>());
assert_eq!(std::any::type_name::<MyList>(), info.type_name());
assert_eq!(std::any::type_name::<usize>(), 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::<MySmallVec>());
assert!(info.item_is::<String>());
assert_eq!(std::any::type_name::<MySmallVec>(), info.type_name());
assert_eq!(std::any::type_name::<String>(), 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::<MyArray>());
assert!(info.item_is::<usize>());
assert_eq!(std::any::type_name::<MyArray>(), info.type_name());
assert_eq!(std::any::type_name::<usize>(), 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::<MyCowStr>());
assert_eq!(std::any::type_name::<MyCowStr>(), info.type_name());
assert_eq!(std::any::type_name::<MyCowStr>(), info.type_path());
} else {
panic!("Expected `TypeInfo::Value`");
}
@ -1464,8 +1447,11 @@ mod tests {
if let TypeInfo::List(info) = info {
assert!(info.is::<MyCowSlice>());
assert!(info.item_is::<u8>());
assert_eq!(std::any::type_name::<MyCowSlice>(), info.type_name());
assert_eq!(std::any::type_name::<u8>(), info.item_type_name());
assert_eq!(std::any::type_name::<MyCowSlice>(), info.type_path());
assert_eq!(
std::any::type_name::<u8>(),
info.item_type_path_table().path()
);
} else {
panic!("Expected `TypeInfo::List`");
}
@ -1482,9 +1468,9 @@ mod tests {
assert!(info.is::<MyMap>());
assert!(info.key_is::<usize>());
assert!(info.value_is::<f32>());
assert_eq!(std::any::type_name::<MyMap>(), info.type_name());
assert_eq!(std::any::type_name::<usize>(), info.key_type_name());
assert_eq!(std::any::type_name::<f32>(), 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::<MyValue>());
assert_eq!(std::any::type_name::<MyValue>(), 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::<Foo<NotTypePath>>();
let registration = registry.get(TypeId::of::<Foo<NotTypePath>>()).unwrap();
assert_eq!("Foo<NotTypePath>", registration.short_name());
assert_eq!(
"Foo<NotTypePath>",
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,

View file

@ -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<TList: List, TItem: FromReflect>() -> Self {
pub fn new<TList: List + TypePath, TItem: FromReflect + TypePath>() -> Self {
Self {
type_name: std::any::type_name::<TList>(),
type_path: TypePathTable::of::<TList>(),
type_id: TypeId::of::<TList>(),
item_type_name: std::any::type_name::<TItem>(),
item_type_path: TypePathTable::of::<TItem>(),
item_type_id: TypeId::of::<TItem>(),
#[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::<T>() == 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::<Self>())
}
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
self.represented_type

View file

@ -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<TMap: Map, TKey: Hash + Reflect, TValue: Reflect>() -> Self {
pub fn new<
TMap: Map + TypePath,
TKey: Hash + Reflect + TypePath,
TValue: Reflect + TypePath,
>() -> Self {
Self {
type_name: std::any::type_name::<TMap>(),
type_path: TypePathTable::of::<TMap>(),
type_id: TypeId::of::<TMap>(),
key_type_name: std::any::type_name::<TKey>(),
key_type_path: TypePathTable::of::<TKey>(),
key_type_id: TypeId::of::<TKey>(),
value_type_name: std::any::type_name::<TValue>(),
value_type_path: TypePathTable::of::<TValue>(),
value_type_id: TypeId::of::<TValue>(),
#[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::<T>() == 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::<T>() == 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::<Self>())
}
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
self.represented_type

View file

@ -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<T: Reflect>(&self) -> bool {
self.type_name() == any::type_name::<T>()
pub fn represents<T: Reflect + TypePath>(&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`

View file

@ -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<E>(self, type_name: &str) -> Result<Self::Value, E>
fn visit_str<E>(self, type_path: &str) -> Result<Self::Value, E>
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::<ReflectDeserialize>() {
@ -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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
@ -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(&registry);
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(&registry);
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(&registry);
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",
),
}"#;

View file

@ -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();

View file

@ -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::<SerializationData>());
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::<SerializationData>());
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, &registry);
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, &registry);
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, &registry);
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, &registry);
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",
),
}"#;

View file

@ -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<T: Reflect>(name: &'static str, fields: &[NamedField]) -> Self {
pub fn new<T: Reflect + TypePath>(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::<T>(),
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
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.
///
/// [type name]: std::any::type_name
pub fn type_name(&self) -> &'static str {
self.type_name
/// 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 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::<Self>())
}
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
self.represented_type
@ -551,7 +543,7 @@ pub fn struct_partial_eq<S: Struct>(a: &S, b: &dyn Reflect) -> Option<bool> {
/// ```
#[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(

View file

@ -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<T: Reflect>(fields: &[UnnamedField]) -> Self {
pub fn new<T: Reflect + TypePath>(fields: &[UnnamedField]) -> Self {
Self {
type_name: std::any::type_name::<T>(),
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
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<Box<dyn Reflect>>,
}
@ -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<dyn Reflect>) {
self.represented_type = None;
self.fields.push(value);
self.generate_name();
}
/// Appends a typed element with value `value` to the tuple.
pub fn insert<T: Reflect>(&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::<Self>()),
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::<Self>()
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::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::<Self, _>(|| {
"(".to_owned() $(+ <$name as TypePath>::type_path())* + ")"
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
"(".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::<Self, _>(|| {
"(".to_owned() + $param::type_path() + ",)"
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
"(".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::<Self, _>(|| {
"(".to_owned() $(+ $param::type_path() + ", ")* + $last::type_path() + ")"
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
"(".to_owned() $(+ $param::short_type_path() + ", ")* + $last::short_type_path() + ")"
})
}
}
};
}
all_tuples!(impl_type_path_tuple, 0, 12, P);

View file

@ -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<T: Reflect>(name: &'static str, fields: &[UnnamedField]) -> Self {
pub fn new<T: Reflect + TypePath>(fields: &[UnnamedField]) -> Self {
Self {
name,
type_name: std::any::type_name::<T>(),
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
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.
///
/// [type name]: std::any::type_name
pub fn type_name(&self) -> &'static str {
self.type_name
/// 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 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::<Self>())
}
#[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);
}

View file

@ -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::<usize >("foo"),
/// NamedField::new::<(f32, f32) >("bar"),
/// ];
/// let info = StructInfo::new::<Self>("MyStruct", &fields);
/// let info = StructInfo::new::<Self>(&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<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
@ -64,15 +67,10 @@ use std::fmt::Debug;
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { 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<T: Any>(&self) -> bool {
TypeId::of::<T>() == 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<T: Reflect + ?Sized>() -> Self {
pub fn new<T: Reflect + TypePath + ?Sized>() -> Self {
Self {
type_name: std::any::type_name::<T>(),
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
#[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.
///
/// [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 value.
///
/// 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 value.

View file

@ -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<PhantomData>`, this is `"core::option::Option<core::marker::PhantomData>"`.
/// For `Option<Vec<usize>>`, this is `"core::option::Option<alloc::vec::Vec<usize>>"`.
fn type_path() -> &'static str;
/// Returns a short, pretty-print enabled path to the type.
///
/// Generic parameter types are also shortened.
///
/// For `Option<PhantomData>`, this is `"Option<PhantomData>"`.
/// For `Option<Vec<usize>>`, this is `"Option<Vec<usize>>"`.
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<PhantomData>`, this is `"Option"`.
/// For `Option<Vec<usize>>`, 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<PhantomData>`, this is `"core"`.
/// For `Option<Vec<usize>>`, 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<PhantomData>`, this is `"core::option"`.
/// For `Option<Vec<usize>>`, 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<T: TypePath> 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<T: TypePath + ?Sized>() -> 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)()
}
}

View file

@ -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<TypeId, TypeRegistration>,
short_name_to_id: HashMap<String, TypeId>,
full_name_to_id: HashMap<String, TypeId>,
ambiguous_names: HashSet<String>,
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::<Option<String>, ReflectSerialize>();
/// type_registry.register_type_data::<Option<String>, ReflectDeserialize>();
/// ```
pub fn register_type_data<T: Reflect + 'static, D: TypeData + FromType<T>>(&mut self) {
pub fn register_type_data<T: Reflect + TypePath, D: TypeData + FromType<T>>(&mut self) {
let data = self.get_mut(TypeId::of::<T>()).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 = T::type_path(),
D = std::any::type_name::<D>(),
)
});
@ -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 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)
/// If no type with the given type path has been registered, returns `None`.
///
/// [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::<Option<String>>();
///
/// assert_eq!("core::option::Option<alloc::string::String>", registration.type_name());
/// assert_eq!("Option<String>", registration.short_name());
/// assert_eq!("core::option::Option<alloc::string::String>", registration.type_info().type_path());
/// assert_eq!("Option<String>", registration.type_info().type_path_table().short_path());
///
/// registration.insert::<ReflectDefault>(FromType::<Option<String>>::from_type());
/// assert!(registration.data::<ReflectDefault>().is_some())
/// ```
///
/// [short name]: bevy_utils::get_short_name
/// [crate-level documentation]: crate
pub struct TypeRegistration {
short_name: String,
data: HashMap<TypeId, Box<dyn TypeData>>,
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<T: Reflect + Typed>() -> Self {
let type_name = std::any::type_name::<T>();
pub fn of<T: Reflect + Typed + TypePath>() -> 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<T: Reflect + erased_serde::Serialize> FromType<T> for ReflectSerialize {
ReflectSerialize {
get_serializable: |value| {
let value = value.downcast_ref::<T>().unwrap_or_else(|| {
panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.type_name(), std::any::type_name::<T>())
panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.reflect_type_path(), std::any::type_name::<T>())
});
Serializable::Borrowed(value)
},
@ -611,9 +599,8 @@ impl<T: Reflect> FromType<T> 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::<Option<f64>>().short_name,
"Option<f64>"
);
assert_eq!(
TypeRegistration::of::<HashMap<u32, String>>().short_name,
"HashMap<u32, String>"
);
assert_eq!(
TypeRegistration::of::<Option<HashMap<u32, String>>>().short_name,
"Option<HashMap<u32, String>>"
);
assert_eq!(
TypeRegistration::of::<Option<HashMap<u32, Option<String>>>>().short_name,
"Option<HashMap<u32, Option<String>>>"
);
assert_eq!(
TypeRegistration::of::<Option<HashMap<String, Option<String>>>>().short_name,
"Option<HashMap<String, Option<String>>>"
);
assert_eq!(
TypeRegistration::of::<Option<HashMap<Option<String>, Option<String>>>>().short_name,
"Option<HashMap<Option<String>, Option<String>>>"
);
assert_eq!(
TypeRegistration::of::<Option<HashMap<Option<String>, (String, Option<String>)>>>()
.short_name,
"Option<HashMap<Option<String>, (String, Option<String>)>>"
);
}
}

View file

@ -62,14 +62,16 @@ mod sealed {
/// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
/// CELL.get_or_set(|| {
/// let fields = [NamedField::new::<i32>("bar")];
/// let info = StructInfo::new::<Self>("Foo", &fields);
/// let info = StructInfo::new::<Self>(&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<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
@ -84,11 +86,6 @@ mod sealed {
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { 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<T: TypedProperty> NonGenericTypeCell<T> {
///
/// struct Foo<T>(T);
///
/// impl<T: Reflect> Typed for Foo<T> {
/// impl<T: Reflect + TypePath> Typed for Foo<T> {
/// fn type_info() -> &'static TypeInfo {
/// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
/// CELL.get_or_insert::<Self, _>(|| {
/// let fields = [UnnamedField::new::<T>(0)];
/// let info = TupleStructInfo::new::<Self>("Foo", &fields);
/// let info = TupleStructInfo::new::<Self>(&fields);
/// TypeInfo::TupleStruct(info)
/// })
/// }
/// }
/// #
/// # impl<T: Reflect> Reflect for Foo<T> {
/// # fn type_name(&self) -> &str { todo!() }
/// # impl<T: TypePath> TypePath for Foo<T> {
/// # fn type_path() -> &'static str { todo!() }
/// # fn short_type_path() -> &'static str { todo!() }
/// # }
/// # impl<T: Reflect + TypePath> Reflect for Foo<T> {
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
@ -160,22 +159,18 @@ impl<T: TypedProperty> NonGenericTypeCell<T> {
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # }
/// # impl<T: Reflect> TypePath for Foo<T> {
/// # 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>(T);
///
/// impl<T: Reflect + TypePath> TypePath for Foo<T> {
/// impl<T: TypePath> TypePath for Foo<T> {
/// fn type_path() -> &'static str {
/// static CELL: GenericTypePathCell = GenericTypePathCell::new();
/// CELL.get_or_insert::<Self, _>(|| format!("my_crate::foo::Foo<{}>", T::type_path()))
@ -185,24 +180,19 @@ impl<T: TypedProperty> NonGenericTypeCell<T> {
/// static CELL: GenericTypePathCell = GenericTypePathCell::new();
/// CELL.get_or_insert::<Self, _>(|| 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<T: Reflect + TypePath> Reflect for Foo<T> {
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { 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<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # }
/// ```
/// [`impl_type_path`]: crate::impl_type_path
/// [`TypePath`]: crate::TypePath

View file

@ -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::<ReflectResource>().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::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_name: component.type_name().to_string(),
type_path: type_info.type_path().to_string(),
}
})?;

View file

@ -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::<ReflectResource>().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::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_name: component_info.name().to_string(),
type_path: registration.type_info().type_path().to_string(),
}
})
})?;

View file

@ -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::<T>()`")]
#[error(
"scene contains the unregistered type `{std_type_name}`. \
consider reflecting it with `#[derive(Reflect)]` \
and registering the type using `app.register_type::<T>()`"
)]
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::<T>()``"
)]
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")]

View file

@ -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!(

View file

@ -21,7 +21,10 @@ fn setup(type_registry: Res<AppTypeRegistry>) {
let type_registry = type_registry.read();
let registration = type_registry.get(TypeId::of::<MyType<u32>>()).unwrap();
info!("Registration for {} exists", registration.short_name());
info!(
"Registration for {} exists",
registration.type_info().type_path(),
);
// MyType<String> was not manually registered, so it does not exist
assert!(type_registry.get(TypeId::of::<MyType<String>>()).is_none());