diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 8dbf71e7faadc..5f683a02e272d 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -17,7 +17,7 @@ use bevy_ecs::{ }; use bevy_hierarchy::Children; use bevy_math::{Quat, Vec3}; -use bevy_reflect::{Reflect, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeUuid}; use bevy_time::Time; use bevy_transform::{prelude::Transform, TransformSystem}; use bevy_utils::{tracing::warn, HashMap}; @@ -60,7 +60,7 @@ pub struct EntityPath { } /// A list of [`VariableCurve`], and the [`EntityPath`] to which they apply. -#[derive(Clone, TypeUuid, Debug, Default)] +#[derive(Clone, TypeUuid, Debug, Default, TypePath)] #[uuid = "d81b7179-0448-4eb0-89fe-c067222725bf"] pub struct AnimationClip { curves: HashMap>, diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 25c2271365544..5558e8c27cfdc 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -943,8 +943,8 @@ impl App { /// See [`bevy_reflect::TypeRegistry::register_type_data`]. #[cfg(feature = "bevy_reflect")] pub fn register_type_data< - T: bevy_reflect::Reflect + 'static, - D: bevy_reflect::TypeData + bevy_reflect::FromType, + T: bevy_reflect::Reflect + bevy_reflect::TypePath + 'static, + D: bevy_reflect::TypeData + bevy_reflect::TypePath + bevy_reflect::FromType, >( &mut self, ) -> &mut Self { diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index 46b2a0f5502e3..357c2842742ef 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -97,7 +97,7 @@ pub struct AssetServerInternal { /// use bevy_asset::{AssetServer, Handle}; /// use bevy_ecs::prelude::{Commands, Res}; /// -/// # #[derive(Debug, bevy_reflect::TypeUuid)] +/// # #[derive(Debug, bevy_reflect::TypeUuid, bevy_reflect::TypePath)] /// # #[uuid = "00000000-0000-0000-0000-000000000000"] /// # struct Image; /// @@ -577,7 +577,7 @@ impl AssetServer { "Failed to find AssetLifecycle for label '{:?}', which has an asset type {} (UUID {:?}). \ Are you sure this asset type has been added to your app builder?", label, - asset_value.type_name(), + asset_value.type_path(), asset_value.type_uuid(), ); } @@ -649,10 +649,10 @@ mod test { use crate::{loader::LoadedAsset, update_asset_storage_system}; use bevy_app::App; use bevy_ecs::prelude::*; - use bevy_reflect::TypeUuid; + use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::BoxedFuture; - #[derive(Debug, TypeUuid)] + #[derive(Debug, TypeUuid, TypePath)] #[uuid = "a5189b72-0572-4290-a2e0-96f73a491c44"] struct PngAsset; diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 97b30dd53d904..f1d92f17efa47 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -420,12 +420,13 @@ macro_rules! load_internal_asset { #[cfg(test)] mod tests { use bevy_app::App; + use bevy_reflect::TypePath; use crate::{AddAsset, Assets}; #[test] fn asset_overwriting() { - #[derive(bevy_reflect::TypeUuid)] + #[derive(bevy_reflect::TypeUuid, TypePath)] #[uuid = "44115972-f31b-46e5-be5c-2b9aece6a52f"] struct MyAsset; let mut app = App::new(); diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index f6a5153b490d9..cd3bf7d778507 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -5,6 +5,7 @@ use crate::{ use anyhow::Error; use anyhow::Result; use bevy_ecs::system::{Res, ResMut}; +use bevy_reflect::TypePath; use bevy_reflect::{TypeUuid, TypeUuidDynamic}; use bevy_utils::{BoxedFuture, HashMap}; use crossbeam_channel::{Receiver, Sender}; @@ -47,13 +48,13 @@ pub trait AssetLoader: Send + Sync + 'static { /// /// In order to load assets into your game you must either add them manually to an asset storage /// with [`Assets::add`] or load them from the filesystem with [`AssetServer::load`]. -pub trait Asset: TypeUuid + AssetDynamic {} +pub trait Asset: TypeUuid + AssetDynamic + TypePath {} /// An untyped version of the [`Asset`] trait. pub trait AssetDynamic: Downcast + TypeUuidDynamic + Send + Sync + 'static {} impl_downcast!(AssetDynamic); -impl Asset for T where T: TypeUuid + AssetDynamic + TypeUuidDynamic {} +impl Asset for T where T: TypeUuid + AssetDynamic + TypeUuidDynamic + TypePath {} impl AssetDynamic for T where T: Send + Sync + 'static + TypeUuidDynamic {} diff --git a/crates/bevy_audio/src/audio_output.rs b/crates/bevy_audio/src/audio_output.rs index e378a2c43f4c3..025fad8c2acf0 100644 --- a/crates/bevy_audio/src/audio_output.rs +++ b/crates/bevy_audio/src/audio_output.rs @@ -1,7 +1,7 @@ use crate::{Audio, AudioSource, Decodable}; use bevy_asset::{Asset, Assets}; use bevy_ecs::system::{NonSend, Res, ResMut}; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::tracing::warn; use rodio::{OutputStream, OutputStreamHandle, Sink, Source}; use std::marker::PhantomData; @@ -116,7 +116,7 @@ pub fn play_queued_audio_system( /// } /// ``` /// -#[derive(TypeUuid)] +#[derive(TypeUuid, TypePath)] #[uuid = "8BEE570C-57C2-4FC0-8CFB-983A22F7D981"] pub struct AudioSink { // This field is an Option in order to allow us to have a safe drop that will detach the sink. diff --git a/crates/bevy_audio/src/audio_source.rs b/crates/bevy_audio/src/audio_source.rs index 217c9aa868350..80f5a72564c51 100644 --- a/crates/bevy_audio/src/audio_source.rs +++ b/crates/bevy_audio/src/audio_source.rs @@ -1,11 +1,11 @@ use anyhow::Result; use bevy_asset::{AssetLoader, LoadContext, LoadedAsset}; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::BoxedFuture; use std::{io::Cursor, sync::Arc}; /// A source of audio data -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "7a14806a-672b-443b-8d16-4f18afefa463"] pub struct AudioSource { /// Raw data of the audio source diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index f86efd9732ffb..c5f40a3de9f55 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -9,7 +9,7 @@ use bevy_app::prelude::*; use bevy_asset::{AddAsset, Handle}; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_pbr::StandardMaterial; -use bevy_reflect::{Reflect, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeUuid}; use bevy_render::mesh::Mesh; use bevy_scene::Scene; @@ -29,7 +29,7 @@ impl Plugin for GltfPlugin { } /// Representation of a loaded glTF file. -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "5c7d5f8a-f7b0-4e45-a09e-406c0372fea2"] pub struct Gltf { pub scenes: Vec>, @@ -49,7 +49,7 @@ pub struct Gltf { /// A glTF node with all of its child nodes, its [`GltfMesh`] and /// [`Transform`](bevy_transform::prelude::Transform). -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "dad74750-1fd6-460f-ac51-0a7937563865"] pub struct GltfNode { pub children: Vec, @@ -58,14 +58,14 @@ pub struct GltfNode { } /// A glTF mesh, which may consist of multiple [`GltfPrimitives`](GltfPrimitive). -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "8ceaec9a-926a-4f29-8ee3-578a69f42315"] pub struct GltfMesh { pub primitives: Vec, } /// Part of a [`GltfMesh`] that consists of a [`Mesh`] and an optional [`StandardMaterial`]. -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "cbfca302-82fd-41cb-af77-cab6b3d50af1"] pub struct GltfPrimitive { pub mesh: Handle, diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 7756fc47af89b..3cbe8399e2c9b 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -1,6 +1,7 @@ use crate::{Axis, Input}; use bevy_ecs::event::{EventReader, EventWriter}; use bevy_ecs::system::{Res, ResMut, Resource}; +use bevy_reflect::TypePath; use bevy_utils::{tracing::info, HashMap, HashSet}; /// A gamepad with an associated `ID`. @@ -291,7 +292,7 @@ pub enum GamepadButtonType { /// ## Updating /// /// The resources are updated inside of the [`gamepad_event_system`]. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TypePath)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct GamepadButton { /// The gamepad on which the button is located on. diff --git a/crates/bevy_input/src/input.rs b/crates/bevy_input/src/input.rs index 90e783ef9723d..58bc41a9b3e06 100644 --- a/crates/bevy_input/src/input.rs +++ b/crates/bevy_input/src/input.rs @@ -1,5 +1,5 @@ use bevy_ecs::system::Resource; -use bevy_reflect::Reflect; +use bevy_reflect::{Reflect, TypePath}; use bevy_utils::HashSet; use std::hash::Hash; @@ -36,7 +36,7 @@ use bevy_ecs::schedule::State; /// * Call the [`Input::clear`] method at each frame start, before processing events. #[derive(Debug, Clone, Resource, Reflect)] #[reflect_value] -pub struct Input { +pub struct Input { /// A collection of every button that is currently being pressed. pressed: HashSet, /// A collection of every button that has just been pressed. @@ -45,7 +45,7 @@ pub struct Input { just_released: HashSet, } -impl Default for Input { +impl Default for Input { fn default() -> Self { Self { pressed: Default::default(), @@ -57,7 +57,7 @@ impl Default for Input { impl Input where - T: Copy + Eq + Hash + Send + Sync + 'static, + T: Copy + Eq + Hash + Send + Sync + TypePath + 'static, { /// Registers a press for the given `input`. pub fn press(&mut self, input: T) { @@ -167,10 +167,12 @@ where #[cfg(test)] mod test { + use bevy_reflect::TypePath; + use crate::Input; /// Used for testing the functionality of [`Input`]. - #[derive(Copy, Clone, Eq, PartialEq, Hash)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, TypePath)] enum DummyInput { Input1, Input2, diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index f77e89e097f47..f5511b59c1199 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -1,6 +1,7 @@ use crate::{ButtonState, Input}; use bevy_ecs::{event::EventReader, system::ResMut}; use bevy_math::Vec2; +use bevy_reflect::TypePath; /// A mouse button input event. /// @@ -29,7 +30,7 @@ pub struct MouseButtonInput { /// ## Updating /// /// The resource is updated inside of the [`mouse_button_input_system`](crate::mouse::mouse_button_input_system). -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, TypePath)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum MouseButton { /// The left mouse button. diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index d744ec77bb041..51f7aad4cdb21 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -17,7 +17,7 @@ use bevy_ecs::{ }, world::FromWorld, }; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ extract_component::ExtractComponentPlugin, mesh::{Mesh, MeshVertexBufferLayout}, @@ -57,11 +57,11 @@ use std::marker::PhantomData; /// ``` /// # use bevy_pbr::{Material, MaterialMeshBundle}; /// # use bevy_ecs::prelude::*; -/// # use bevy_reflect::TypeUuid; +/// # use bevy_reflect::{TypeUuid, TypePath}; /// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image, color::Color}; /// # use bevy_asset::{Handle, AssetServer, Assets}; /// -/// #[derive(AsBindGroup, TypeUuid, Debug, Clone)] +/// #[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] /// #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] /// pub struct CustomMaterial { /// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to @@ -108,7 +108,9 @@ use std::marker::PhantomData; /// @group(1) @binding(2) /// var color_sampler: sampler; /// ``` -pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'static { +pub trait Material: + AsBindGroup + Send + Sync + Clone + TypeUuid + TypePath + Sized + 'static +{ /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader /// will be used. fn vertex_shader() -> ShaderRef { diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index db61a1fe70e38..e5592b136b526 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -1,7 +1,7 @@ use crate::{AlphaMode, Material, MaterialPipeline, MaterialPipelineKey, PBR_SHADER_HANDLE}; use bevy_asset::Handle; use bevy_math::Vec4; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ color::Color, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*, texture::Image, @@ -12,7 +12,7 @@ use bevy_render::{ /// . /// /// May be created directly from a [`Color`] or an [`Image`]. -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "7494888b-c082-457b-aacf-517228cc0c22"] #[bind_group_data(StandardMaterialKey)] #[uniform(0, StandardMaterialUniform)] diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 9fb4295ea7e70..5d8a70bdc32bb 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -4,10 +4,15 @@ use crate::utility::members_to_serialization_denylist; use bit_set::BitSet; use quote::quote; -use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; +use crate::{ + utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_PATH_ATTRIBUTE_NAME, +}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant}; +use syn::{ + Data, DeriveInput, Field, Fields, Generics, Ident, Lit, Meta, MetaList, NestedMeta, Path, + Token, Variant, +}; pub(crate) enum ReflectDerive<'a> { Struct(ReflectStruct<'a>), @@ -37,6 +42,8 @@ pub(crate) struct ReflectMeta<'a> { type_name: &'a Ident, /// The generics defined on this type. generics: &'a Generics, + /// User defined options for the impl of `TypePath`. + type_path_options: TypePathOptions, /// A cached instance of the path to the `bevy_reflect` crate. bevy_reflect_path: Path, } @@ -114,6 +121,8 @@ impl<'a> ReflectDerive<'a> { // Should indicate whether `#[reflect_value]` was used let mut force_reflect_value = false; + let mut type_path_options = None; + for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { match attribute { Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => { @@ -126,6 +135,9 @@ impl<'a> ReflectDerive<'a> { Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => { force_reflect_value = true; } + Meta::List(meta_list) if meta_list.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => { + type_path_options = Some(TypePathOptions::parse_meta_list(meta_list)?); + } _ => continue, } } @@ -134,13 +146,19 @@ impl<'a> ReflectDerive<'a> { &input.ident, &input.generics, traits, + type_path_options.unwrap_or_default(), ))); } return match &input.data { Data::Struct(data) => { let fields = Self::collect_struct_fields(&data.fields)?; - let meta = ReflectMeta::new(&input.ident, &input.generics, traits); + let meta = ReflectMeta::new( + &input.ident, + &input.generics, + traits, + type_path_options.unwrap_or_default(), + ); let reflect_struct = ReflectStruct { meta, serialization_denylist: members_to_serialization_denylist( @@ -157,7 +175,12 @@ impl<'a> ReflectDerive<'a> { } Data::Enum(data) => { let variants = Self::collect_enum_variants(&data.variants)?; - let meta = ReflectMeta::new(&input.ident, &input.generics, traits); + let meta = ReflectMeta::new( + &input.ident, + &input.generics, + traits, + type_path_options.unwrap_or_default(), + ); let reflect_enum = ReflectEnum { meta, variants }; Ok(Self::Enum(reflect_enum)) @@ -220,11 +243,17 @@ impl<'a> ReflectDerive<'a> { } impl<'a> ReflectMeta<'a> { - pub fn new(type_name: &'a Ident, generics: &'a Generics, traits: ReflectTraits) -> Self { + pub fn new( + type_name: &'a Ident, + generics: &'a Generics, + traits: ReflectTraits, + type_path_options: TypePathOptions, + ) -> Self { Self { traits, type_name, generics, + type_path_options, bevy_reflect_path: utility::get_bevy_reflect_path(), } } @@ -244,6 +273,11 @@ impl<'a> ReflectMeta<'a> { self.generics } + /// User defined options for the impl of `TypePath`. + pub fn type_path_options(&self) -> &TypePathOptions { + &self.type_path_options + } + /// The cached `bevy_reflect` path. pub fn bevy_reflect_path(&self) -> &Path { &self.bevy_reflect_path @@ -339,3 +373,78 @@ impl<'a> ReflectEnum<'a> { &self.variants } } + +/// User defined options for the impl of `TypePath`. +#[derive(Default)] +pub(crate) struct TypePathOptions { + /// If set, the custom module path. + pub module_path: Option, + /// If set, the custom type ident. + pub type_ident: Option, +} + +impl TypePathOptions { + fn parse_meta_list(meta_list: MetaList) -> Result { + fn expected_literal_string(lit: &Lit) -> Result { + match lit { + Lit::Str(lit_str) => Ok(lit_str.value()), + other => Err(syn::Error::new(other.span(), "Expected a str literal")), + } + } + + fn is_valid_module_path(_module_path: &str) -> bool { + // FIXME: what conditions here ? + true + } + + fn is_valid_type_ident(_type_ident: &str) -> bool { + // FIXME: what conditions here ? + true + } + + let mut module_path = None; + let mut type_ident = None; + + for attribute in meta_list.nested { + match attribute { + NestedMeta::Meta(Meta::NameValue(name_value)) + if name_value.path.is_ident("path") => + { + let name = expected_literal_string(&name_value.lit)?; + if is_valid_module_path(&name) { + module_path = Some(name); + } else { + return Err(syn::Error::new( + name_value.lit.span(), + "Expected a valid module path", + )); + } + } + NestedMeta::Meta(Meta::NameValue(name_value)) + if name_value.path.is_ident("ident") => + { + let name = expected_literal_string(&name_value.lit)?; + if is_valid_type_ident(&name) { + type_ident = Some(name); + } else { + return Err(syn::Error::new( + name_value.lit.span(), + "Expected a valid type ident", + )); + } + } + other => { + return Err(syn::Error::new( + other.span(), + format!("Unexpected entry for the `{TYPE_PATH_ATTRIBUTE_NAME}` attribute. Usage: #[{TYPE_PATH_ATTRIBUTE_NAME}(path = \"my_crate::my_module\", ident = \"MyType\")]"), + )); + } + } + } + + Ok(Self { + module_path, + type_ident, + }) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index 73e4cb6451108..83662172fe70a 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -51,7 +51,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #ref_value.reflect_ref() { match #ref_value.variant_name() { #(#variant_names => Some(#variant_constructors),)* - name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), + name => panic!("variant with name `{}` does not exist on enum `{}`", name, ::type_path()), } } else { None diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 8a7afb792eef0..065b8c8a06b1b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,6 +1,6 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::quote; @@ -66,6 +66,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { bevy_reflect_path, ); + let type_path_impl = impl_type_path(reflect_enum.meta()); + let get_type_registration_impl = reflect_enum.meta().get_type_registration(); let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().generics().split_for_impl(); @@ -75,6 +77,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { #typed_impl + #type_path_impl + impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { fn field(&self, #ref_name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { match self { @@ -161,8 +165,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_clause { #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() + fn type_path(&self) -> &str { + ::type_path() } #[inline] @@ -231,11 +235,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { #(#variant_names => { *self = #variant_constructors })* - name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), + name => panic!("variant with name `{}` does not exist on enum `{}`", name, self.type_path()), } } } else { - panic!("`{}` is not an enum", #ref_value.type_name()); + panic!("`{}` is not an enum", #ref_value.type_path()); } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs index 19523fbf806ba..8cda6b79644f8 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs @@ -1,11 +1,13 @@ mod enums; mod structs; mod tuple_structs; +mod type_path; mod typed; mod values; pub(crate) use enums::impl_enum; pub(crate) use structs::impl_struct; pub(crate) use tuple_structs::impl_tuple_struct; +pub(crate) use type_path::impl_type_path; pub(crate) use typed::impl_typed; pub(crate) use values::impl_value; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index c68aab2e2d4c3..8bb953cedae81 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -1,4 +1,4 @@ -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::quote; @@ -65,7 +65,10 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { bevy_reflect_path, ); + let type_path_impl = impl_type_path(reflect_struct.meta()); + let get_type_registration_impl = reflect_struct.get_type_registration(); + let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); @@ -74,6 +77,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { #typed_impl + #type_path_impl + impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { match name { @@ -120,7 +125,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); - dynamic.set_name(self.type_name().to_string()); + dynamic.set_name(self.type_path().to_string()); #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* dynamic } @@ -128,8 +133,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() + fn type_path(&self) -> &str { + ::type_path() } #[inline] diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index bb44fc5f231aa..f22aa13cbdfa7 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -1,4 +1,4 @@ -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use crate::ReflectStruct; use proc_macro::TokenStream; use quote::quote; @@ -49,6 +49,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { bevy_reflect_path, ); + let type_path_impl = impl_type_path(reflect_struct.meta()); + let (impl_generics, ty_generics, where_clause) = reflect_struct.meta().generics().split_for_impl(); @@ -57,6 +59,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { #typed_impl + #type_path_impl + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { match index { @@ -82,7 +86,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); - dynamic.set_name(self.type_name().to_string()); + dynamic.set_name(self.type_path().to_string()); #(dynamic.insert_boxed(self.#field_idents.clone_value());)* dynamic } @@ -90,8 +94,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() + fn type_path(&self) -> &str { + ::type_path() } #[inline] diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/type_path.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/type_path.rs new file mode 100644 index 0000000000000..1ad2d9e61abff --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/type_path.rs @@ -0,0 +1,113 @@ +use quote::quote; + +use crate::derive_data::ReflectMeta; + +pub(crate) fn impl_type_path(reflect_meta: &ReflectMeta) -> proc_macro2::TokenStream { + let generics = reflect_meta.generics(); + let type_name = reflect_meta.type_name(); + let bevy_reflect_path = reflect_meta.bevy_reflect_path(); + let type_path_options = reflect_meta.type_path_options(); + + let is_generic = !generics.params.is_empty(); + + let module_path = match type_path_options.module_path.as_ref() { + Some(x) if x.is_empty() => None, + Some(x) => Some(quote!(#x)), + None => Some(quote!(module_path!())), + }; + + let module_path_len = match module_path.as_ref() { + Some(module_path) => quote!(#module_path.len()), + None => quote!(0), + }; + + let module_path_2columns = match module_path.as_ref() { + Some(module_path) => quote!(concat!(#module_path, "::")), + None => quote!(""), + }; + + let crate_name_len = match module_path { + Some(module_path) => quote!(#bevy_reflect_path::utility::crate_name_len(#module_path)), + None => quote!(0), + }; + + let type_ident = type_path_options + .type_ident + .as_ref() + .map(|x| quote!(#x)) + .unwrap_or_else(|| { + let type_name = type_name.to_string(); + quote!(#type_name) + }); + + let get_type_path = if is_generic { + let values = { + let getters = generics.params.iter().map(|p| match p { + syn::GenericParam::Type(p) => { + let ty = &p.ident; + quote!(<#ty as #bevy_reflect_path::TypePath>::type_path()) + } + syn::GenericParam::Lifetime(p) => { + let name = format!("'{}", p.lifetime.ident); + quote!(#name) + } + syn::GenericParam::Const(p) => { + let name = &p.ident; + quote!(#name) + } + }); + + quote!(#(#getters),*) + }; + + let brackets = { + let brackets = vec![quote!({}); generics.params.len()]; + quote!(#(#brackets),*).to_string() + }; + + quote! { + static CELL: #bevy_reflect_path::utility::GenericTypePathCell = #bevy_reflect_path::utility::GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + format!(concat!(#module_path_2columns, #type_ident, "<", #brackets, ">"), #values) + }) + } + } else { + quote! { + concat!(#module_path_2columns, #type_ident) + } + }; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + quote! { + impl #impl_generics #bevy_reflect_path::TypePath for #type_name #ty_generics #where_clause { + #[inline] + fn type_path() -> &'static str { + #get_type_path + } + + #[inline] + fn short_type_name_base() -> &'static str { + const IDENT_POS: usize = #module_path_len + 2; + const GENERIC_POS: usize = IDENT_POS + #type_ident.len(); + &::type_path()[IDENT_POS..GENERIC_POS] + } + + #[inline] + fn short_type_name() -> &'static str { + const IDENT_POS: usize = #module_path_len + 2; + &::type_path()[IDENT_POS..] + } + + #[inline] + fn module_path() -> &'static str { + &::type_path()[..#module_path_len] + } + + #[inline] + fn crate_name() -> &'static str { + &::type_path()[..#crate_name_len] + } + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 5de59917959fd..8cab7bd64bd28 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -1,4 +1,4 @@ -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use crate::ReflectMeta; use proc_macro::TokenStream; use quote::quote; @@ -22,6 +22,8 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { bevy_reflect_path, ); + let type_path_impl = impl_type_path(meta); + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); let get_type_registration_impl = meta.get_type_registration(); @@ -30,10 +32,12 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #typed_impl + #type_path_impl + impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() + fn type_path(&self) -> &str { + ::type_path() } #[inline] @@ -77,7 +81,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { if let Some(value) = value.downcast_ref::() { *self = std::clone::Clone::clone(value); } else { - panic!("Value is not {}.", std::any::type_name::()); + panic!("Value is not {}.", self.type_path()); } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index d7a7515683a11..3e17ef28fedbf 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -23,20 +23,24 @@ mod impls; mod reflect_value; mod registration; mod trait_reflection; +mod type_path; mod type_uuid; mod utility; use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct}; +use derive_data::TypePathOptions; use proc_macro::TokenStream; use quote::quote; -use reflect_value::ReflectValueDef; +use reflect_value::{NamedReflectValueDef, ReflectValueDef}; use syn::spanned::Spanned; use syn::{parse_macro_input, DeriveInput}; +use type_path::TypePathDef; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; +pub(crate) static TYPE_PATH_ATTRIBUTE_NAME: &str = "type_path"; -#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] +#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module, type_path))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); @@ -55,6 +59,73 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { } } +/// Implement `TypePath` on a type. +/// +/// ## Type name convention +/// +/// The type path is the module path followed by the ident of the type. +/// If the type is generic the type path of its generic parameters are included between `<` and `>`. +/// +/// See examples. +/// +/// ## Custom type path +/// +/// It's possible to override the default behaviour and choose a custom type path by using +/// the `type_path` attribute. +/// +/// If specifying a custom type path, it's recommended to prefix it with the name of your crate. +/// +/// It's highly discouraged to use an unprefixed type path that could collide with another type +/// or a malformed type path (e.g. `BlAH@blah blah`). +/// +/// ## Example +/// +/// ```ignore +/// # bevy_reflect::TypePath; +/// +/// mod a { +/// pub mod b { +/// pub mod c { +/// #[derive(TypePath)] +/// pub struct ABC; +/// } +/// +/// #[derive(TypePath)] +/// #[type_path(path = "my_lib")] +/// pub struct AB(T); +/// } +/// +/// #[derive(TypePath)] +/// pub struct A(N); +/// } +/// +/// # use a::A; +/// # use a::b::AB; +/// # use a::b::c::ABC; +/// +/// assert_eq!(ABC::type_path(), "a::b::c::ABC"); +/// assert_eq!(AB::::type_path(), "my_lib::AB"); +/// assert_eq!(A::<5>::type_path(), "a::A<5>"); +/// ``` +#[proc_macro_derive(TypePath, attributes(module, type_path))] +pub fn derive_type_path(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let derive_data = match ReflectDerive::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), + }; + + match derive_data { + ReflectDerive::TupleStruct(struct_data) + | ReflectDerive::Struct(struct_data) + | ReflectDerive::UnitStruct(struct_data) => impls::impl_type_path(struct_data.meta()), + ReflectDerive::Enum(meta) => impls::impl_type_path(meta.meta()), + ReflectDerive::Value(meta) => impls::impl_type_path(&meta), + } + .into() +} + /// Derives the `FromReflect` trait. /// /// This macro supports the following field attributes: @@ -94,11 +165,13 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { - let def = parse_macro_input!(input as ReflectValueDef); + let def = parse_macro_input!(input as NamedReflectValueDef); + impls::impl_value(&ReflectMeta::new( - &def.type_name, - &def.generics, - def.traits.unwrap_or_default(), + &def.def.type_name, + &def.def.generics, + def.def.traits.unwrap_or_default(), + def.type_path_options, )) } @@ -175,5 +248,33 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { &def.type_name, &def.generics, def.traits.unwrap_or_default(), + TypePathOptions::default(), )) } + +/// A replacement for `#[derive(TypePath)]` to be used with foreign types which +/// the definitions of cannot be altered. +/// +/// But unlike `#[derive(TypePath)]` that prefix the type path with the module path +/// using the macro [`module_path`], `impl_type_path` only uses the ident of the type +/// as the type path. +/// +/// # Example +/// Implementing `TypePath` for `Vec`: +/// ```ignore +/// impl_type_path!(Vec); +/// ``` +#[proc_macro] +pub fn impl_type_path(input: TokenStream) -> TokenStream { + let def = parse_macro_input!(input as TypePathDef); + let meta = ReflectMeta::new( + &def.type_name, + &def.generics, + Default::default(), + TypePathOptions { + module_path: Some("".to_string()), + type_ident: None, + }, + ); + TokenStream::from(impls::impl_type_path(&meta)) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index ec54b99a6f404..7ecf69f25c81f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,8 +1,9 @@ use crate::container_attributes::ReflectTraits; +use crate::derive_data::TypePathOptions; use proc_macro2::Ident; use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; -use syn::{parenthesized, Generics}; +use syn::{parenthesized, Generics, LitStr, Token}; /// A struct used to define a simple reflected value type (such as primitives). /// @@ -52,3 +53,37 @@ impl Parse for ReflectValueDef { }) } } + +/// A [`ReflectValueDef`] that allow an optional custom type path in front. +/// +/// # Example +/// +/// ```ignore +/// impl_reflect_value!(@"my_lib::my_module" Foo where T1: Bar (TraitA, TraitB)); +/// ``` +pub(crate) struct NamedReflectValueDef { + pub type_path_options: TypePathOptions, + pub def: ReflectValueDef, +} + +impl Parse for NamedReflectValueDef { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + let mut module_path = None; + if lookahead.peek(Token![@]) { + let _at: Token![@] = input.parse()?; + let name: LitStr = input.parse()?; + module_path = Some(name.value()); + } + + let def = input.parse()?; + + Ok(Self { + type_path_options: TypePathOptions { + module_path: module_path.or_else(|| Some(String::new())), + type_ident: None, + }, + def, + }) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs b/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs new file mode 100644 index 0000000000000..f288d2678e957 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs @@ -0,0 +1,20 @@ +use proc_macro2::Ident; +use syn::parse::{Parse, ParseStream}; +use syn::Generics; + +pub(crate) struct TypePathDef { + pub type_name: Ident, + pub generics: Generics, +} + +impl Parse for TypePathDef { + fn parse(input: ParseStream) -> syn::Result { + let type_name = input.parse::()?; + let generics = input.parse::()?; + + Ok(Self { + type_name, + generics, + }) + } +} diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index e8bdff2ad3b9a..2e14a77120cfe 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,5 +1,6 @@ use crate::{ - utility::NonGenericTypeInfoCell, DynamicInfo, Reflect, ReflectMut, ReflectRef, TypeInfo, Typed, + self as bevy_reflect, utility::NonGenericTypeInfoCell, DynamicInfo, Reflect, ReflectMut, + ReflectRef, TypeInfo, TypePath, Typed, }; use std::{ any::{Any, TypeId}, @@ -35,7 +36,7 @@ pub trait Array: Reflect { fn clone_dynamic(&self) -> DynamicArray { DynamicArray { - name: self.type_name().to_string(), + name: self.type_path().to_string(), values: self.iter().map(|value| value.clone_value()).collect(), } } @@ -44,9 +45,9 @@ pub trait Array: Reflect { /// A container for compile-time array info. #[derive(Clone, Debug)] pub struct ArrayInfo { - type_name: &'static str, + type_path: &'static str, type_id: TypeId, - item_type_name: &'static str, + item_type_path: &'static str, item_type_id: TypeId, capacity: usize, } @@ -58,11 +59,11 @@ impl ArrayInfo { /// /// * `capacity`: The maximum capacity of the underlying array. /// - pub fn new(capacity: usize) -> Self { + pub fn new(capacity: usize) -> Self { Self { - type_name: std::any::type_name::(), + type_path: ::type_path(), type_id: TypeId::of::(), - item_type_name: std::any::type_name::(), + item_type_path: ::type_path(), item_type_id: TypeId::of::(), capacity, } @@ -73,11 +74,11 @@ impl ArrayInfo { self.capacity } - /// The [type name] of the array. + /// The [type path] of the array. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the array. @@ -90,11 +91,11 @@ impl ArrayInfo { TypeId::of::() == self.type_id } - /// The [type name] of the array item. + /// 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 + /// [type path]: TypePath + pub fn item_type_path(&self) -> &'static str { + self.item_type_path } /// The [`TypeId`] of the array item. @@ -117,7 +118,7 @@ impl ArrayInfo { /// can be mutated— just that the _number_ of items cannot change. /// /// [`DynamicList`]: crate::DynamicList -#[derive(Debug)] +#[derive(Debug, TypePath)] pub struct DynamicArray { pub(crate) name: String, pub(crate) values: Box<[Box]>, @@ -156,8 +157,8 @@ impl DynamicArray { impl Reflect for DynamicArray { #[inline] - fn type_name(&self) -> &str { - self.name.as_str() + fn type_path(&self) -> &str { + &self.name } #[inline] diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index e0ad08a689406..69700c47a829a 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -1,7 +1,10 @@ +use bevy_reflect_derive::TypePath; + use crate::utility::NonGenericTypeInfoCell; use crate::{ - enum_debug, enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, DynamicTuple, Enum, - Reflect, ReflectMut, ReflectRef, Struct, Tuple, TypeInfo, Typed, VariantFieldIter, VariantType, + self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, + DynamicTuple, Enum, Reflect, ReflectMut, ReflectRef, Struct, Tuple, TypeInfo, Typed, + VariantFieldIter, VariantType, }; use std::any::Any; use std::fmt::Formatter; @@ -62,7 +65,7 @@ impl From<()> for DynamicVariant { /// /// // Create a DynamicEnum to represent the new value /// let mut dyn_enum = DynamicEnum::new( -/// Reflect::type_name(&value), +/// Reflect::type_path(&value), /// "None", /// DynamicVariant::Unit /// ); @@ -73,7 +76,7 @@ impl From<()> for DynamicVariant { /// // Tada! /// assert_eq!(None, value); /// ``` -#[derive(Default, Debug)] +#[derive(Default, Debug, TypePath)] pub struct DynamicEnum { name: String, variant_name: String, @@ -126,12 +129,12 @@ impl DynamicEnum { } } - /// Returns the type name of the enum. + /// Returns the type path of the enum. pub fn name(&self) -> &str { &self.name } - /// Sets the type name of the enum. + /// Sets the type path of the enum. pub fn set_name(&mut self, name: String) { self.name = name; } @@ -167,7 +170,7 @@ impl DynamicEnum { pub fn from_ref(value: &TEnum) -> Self { match value.variant_type() { VariantType::Unit => DynamicEnum::new_with_index( - value.type_name(), + value.type_path(), value.variant_index(), value.variant_name(), DynamicVariant::Unit, @@ -178,7 +181,7 @@ impl DynamicEnum { data.insert_boxed(field.value().clone_value()); } DynamicEnum::new_with_index( - value.type_name(), + value.type_path(), value.variant_index(), value.variant_name(), DynamicVariant::Tuple(data), @@ -191,7 +194,7 @@ impl DynamicEnum { data.insert_boxed(name, field.value().clone_value()); } DynamicEnum::new_with_index( - value.type_name(), + value.type_path(), value.variant_index(), value.variant_name(), DynamicVariant::Struct(data), @@ -290,7 +293,7 @@ impl Enum for DynamicEnum { impl Reflect for DynamicEnum { #[inline] - fn type_name(&self) -> &str { + fn type_path(&self) -> &str { &self.name } @@ -370,7 +373,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.type_path()); } } diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index ab3fd03dd05e1..0d250a106fe8b 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,4 +1,4 @@ -use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; +use crate::{type_path, DynamicEnum, Reflect, TypePath, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::slice::Iter; @@ -125,7 +125,7 @@ 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.type_path(), self.variant_name()) } } @@ -133,7 +133,7 @@ pub trait Enum: Reflect { #[derive(Clone, Debug)] pub struct EnumInfo { name: &'static str, - type_name: &'static str, + type_path: &'static str, type_id: TypeId, variants: Box<[VariantInfo]>, variant_indices: HashMap<&'static str, usize>, @@ -147,7 +147,7 @@ impl EnumInfo { /// * `name`: The name of this enum (_without_ generics or lifetimes) /// * `variants`: The variants of this enum in the order they are defined /// - pub fn new(name: &'static str, variants: &[VariantInfo]) -> Self { + pub fn new(name: &'static str, variants: &[VariantInfo]) -> Self { let variant_indices = variants .iter() .enumerate() @@ -156,7 +156,7 @@ impl EnumInfo { Self { name, - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), variants: variants.to_vec().into_boxed_slice(), variant_indices, @@ -184,7 +184,7 @@ impl EnumInfo { /// /// This does _not_ check if the given variant exists. pub fn variant_path(&self, name: &str) -> String { - format!("{}::{}", self.type_name(), name) + format!("{}::{}", self.type_path(), name) } /// Checks if a variant with the given name exists within this enum. @@ -211,11 +211,11 @@ impl EnumInfo { self.name } - /// The [type name] of the enum. + /// The [type path] of the enum. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: crate::TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the enum. diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index e8c32e73ef9c5..b17e951823234 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -25,7 +25,7 @@ mod tests { let info = MyEnum::type_info(); if let TypeInfo::Enum(info) = info { assert!(info.is::(), "expected type to be `MyEnum`"); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(::type_path(), info.type_path()); // === MyEnum::A === // assert_eq!("A", info.variant_at(0).unwrap().name()); @@ -316,7 +316,7 @@ mod tests { #[test] fn enum_should_allow_generics() { #[derive(Reflect, Debug, PartialEq)] - enum TestEnum { + enum TestEnum { A, B(T), C { value: T }, @@ -342,14 +342,14 @@ mod tests { // === Tuple === // let mut data = DynamicTuple::default(); data.insert(1.23_f32); - let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "B", data); + let dyn_enum = DynamicEnum::new( as TypePath>::type_path(), "B", data); value.apply(&dyn_enum); assert_eq!(TestEnum::B(1.23), value); // === Struct === // let mut data = DynamicStruct::default(); data.insert("value", 1.23_f32); - let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "C", data); + let dyn_enum = DynamicEnum::new( as TypePath>::type_path(), "C", data); value.apply(&dyn_enum); assert_eq!(TestEnum::C { value: 1.23 }, value); } @@ -371,14 +371,14 @@ mod tests { // === Tuple === // let mut data = DynamicTuple::default(); data.insert(TestStruct(123)); - let dyn_enum = DynamicEnum::new(std::any::type_name::(), "B", data); + let dyn_enum = DynamicEnum::new(::type_path(), "B", data); value.apply(&dyn_enum); assert_eq!(TestEnum::B(TestStruct(123)), value); // === Struct === // let mut data = DynamicStruct::default(); data.insert("value", TestStruct(123)); - let dyn_enum = DynamicEnum::new(std::any::type_name::(), "C", data); + let dyn_enum = DynamicEnum::new(::type_path(), "C", data); value.apply(&dyn_enum); assert_eq!( TestEnum::C { @@ -409,14 +409,14 @@ mod tests { // === Tuple === // let mut data = DynamicTuple::default(); data.insert(OtherEnum::B(123)); - let dyn_enum = DynamicEnum::new(std::any::type_name::(), "B", data); + let dyn_enum = DynamicEnum::new(::type_path(), "B", data); value.apply(&dyn_enum); assert_eq!(TestEnum::B(OtherEnum::B(123)), value); // === Struct === // let mut data = DynamicStruct::default(); data.insert("value", OtherEnum::C { value: 1.23 }); - let dyn_enum = DynamicEnum::new(std::any::type_name::(), "C", data); + let dyn_enum = DynamicEnum::new(::type_path(), "C", data); value.apply(&dyn_enum); assert_eq!( TestEnum::C { diff --git a/crates/bevy_reflect/src/fields.rs b/crates/bevy_reflect/src/fields.rs index f70e5c4c686d4..b602783046021 100644 --- a/crates/bevy_reflect/src/fields.rs +++ b/crates/bevy_reflect/src/fields.rs @@ -1,20 +1,20 @@ -use crate::Reflect; +use crate::{type_path, Reflect, TypePath}; 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: &'static str, type_id: TypeId, } impl NamedField { /// Create a new [`NamedField`]. - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str) -> Self { Self { name, - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), } } @@ -24,11 +24,11 @@ impl NamedField { self.name } - /// The [type name] of the field. + /// The [type path] of the field. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the field. @@ -46,15 +46,15 @@ impl NamedField { #[derive(Clone, Debug)] pub struct UnnamedField { index: usize, - type_name: &'static str, + type_path: &'static str, type_id: TypeId, } impl UnnamedField { - pub fn new(index: usize) -> Self { + pub fn new(index: usize) -> Self { Self { index, - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), } } @@ -64,11 +64,11 @@ impl UnnamedField { self.index } - /// The [type name] of the field. + /// The [type path] of the field. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the field. diff --git a/crates/bevy_reflect/src/impls/glam.rs b/crates/bevy_reflect/src/impls/glam.rs index 9dfcc8293ce23..5fbdc3f23639b 100644 --- a/crates/bevy_reflect/src/impls/glam.rs +++ b/crates/bevy_reflect/src/impls/glam.rs @@ -7,6 +7,7 @@ use glam::*; impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct IVec2 { x: i32, y: i32, @@ -14,6 +15,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct IVec3 { x: i32, y: i32, @@ -22,6 +24,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct IVec4 { x: i32, y: i32, @@ -32,6 +35,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct UVec2 { x: u32, y: u32, @@ -39,6 +43,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct UVec3 { x: u32, y: u32, @@ -47,6 +52,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct UVec4 { x: u32, y: u32, @@ -57,6 +63,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct Vec2 { x: f32, y: f32, @@ -64,6 +71,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct Vec3 { x: f32, y: f32, @@ -72,6 +80,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct Vec3A { x: f32, y: f32, @@ -80,6 +89,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct Vec4 { x: f32, y: f32, @@ -90,6 +100,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct BVec2 { x: bool, y: bool, @@ -97,6 +108,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct BVec3 { x: bool, y: bool, @@ -105,6 +117,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct BVec4 { x: bool, y: bool, @@ -115,6 +128,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct DVec2 { x: f64, y: f64, @@ -122,6 +136,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct DVec3 { x: f64, y: f64, @@ -130,6 +145,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path(path = "glam")] struct DVec4 { x: f64, y: f64, @@ -140,6 +156,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct Mat2 { x_axis: Vec2, y_axis: Vec2, @@ -147,6 +164,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct Mat3 { x_axis: Vec3, y_axis: Vec3, @@ -155,6 +173,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct Mat3A { x_axis: Vec3A, y_axis: Vec3A, @@ -163,6 +182,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct Mat4 { x_axis: Vec4, y_axis: Vec4, @@ -173,6 +193,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct DMat2 { x_axis: DVec2, y_axis: DVec2, @@ -180,6 +201,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct DMat3 { x_axis: DVec3, y_axis: DVec3, @@ -188,6 +210,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct DMat4 { x_axis: DVec4, y_axis: DVec4, @@ -198,6 +221,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct Affine2 { matrix2: Mat2, translation: Vec2, @@ -205,6 +229,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct Affine3A { matrix3: Mat3A, translation: Vec3A, @@ -213,6 +238,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct DAffine2 { matrix2: DMat2, translation: DVec2, @@ -220,6 +246,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path(path = "glam")] struct DAffine3 { matrix3: DMat3, translation: DVec3, @@ -230,18 +257,72 @@ impl_reflect_struct!( // mechanisms for read-only fields. I doubt those mechanisms would be added, // so for now quaternions will remain as values. They are represented identically // to Vec4 and DVec4, so you may use those instead and convert between. -impl_reflect_value!(Quat(Debug, PartialEq, Serialize, Deserialize, Default)); -impl_reflect_value!(DQuat(Debug, PartialEq, Serialize, Deserialize, Default)); +impl_reflect_value!(@"glam" Quat(Debug, PartialEq, Serialize, Deserialize, Default)); +impl_reflect_value!(@"glam" DQuat(Debug, PartialEq, Serialize, Deserialize, Default)); impl_from_reflect_value!(Quat); impl_from_reflect_value!(DQuat); -impl_reflect_value!(EulerRot(Debug, Default)); +impl_reflect_value!(@"glam" EulerRot(Debug, Default)); // glam type aliases these to the non simd versions when there is no support (this breaks wasm builds for example) // ideally it shouldn't do that and there's an issue on glam for this // https://github.com/bitshifter/glam-rs/issues/306 #[cfg(any(target_feature = "sse2", target_feature = "simd128"))] -impl_reflect_value!(BVec3A(Debug, PartialEq, Default)); +impl_reflect_value!(@"glam" BVec3A(Debug, PartialEq, Default)); #[cfg(any(target_feature = "sse2", target_feature = "simd128"))] -impl_reflect_value!(BVec4A(Debug, PartialEq, Default)); +impl_reflect_value!(@"glam" BVec4A(Debug, PartialEq, Default)); + +#[cfg(test)] +mod tests { + use glam::*; + + use crate::TypePath; + + #[test] + fn type_name_should_be_prefixed_with_glam() { + macro_rules! assert_name { + ($t:ty) => { + assert_eq!( + <$t as TypePath>::type_path(), + concat!("glam::", stringify!($t)) + ) + }; + } + + assert_name!(IVec2); + assert_name!(IVec3); + assert_name!(IVec4); + assert_name!(UVec2); + assert_name!(UVec3); + assert_name!(UVec4); + assert_name!(Vec2); + assert_name!(Vec3); + assert_name!(Vec3A); + assert_name!(Vec4); + assert_name!(BVec2); + assert_name!(BVec3); + assert_name!(BVec4); + assert_name!(DVec2); + assert_name!(DVec3); + assert_name!(DVec4); + assert_name!(Mat2); + assert_name!(Mat3); + assert_name!(Mat3A); + assert_name!(Mat4); + assert_name!(DMat2); + assert_name!(DMat3); + assert_name!(DMat4); + assert_name!(Affine2); + assert_name!(Affine3A); + assert_name!(DAffine2); + assert_name!(DAffine2); + assert_name!(Quat); + assert_name!(DQuat); + assert_name!(EulerRot); + #[cfg(any(target_feature = "sse2", target_feature = "simd128"))] + assert_name!(BVec3A); + #[cfg(any(target_feature = "sse2", target_feature = "simd128"))] + assert_name!(BVec4A); + } +} diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index ef855e9426379..3951b9801d8a1 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -1,15 +1,19 @@ +use bevy_reflect_derive::impl_type_path; use smallvec::SmallVec; use std::any::Any; use crate::utility::GenericTypeInfoCell; use crate::{ - Array, ArrayIter, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Reflect, - ReflectFromPtr, ReflectMut, ReflectRef, TypeInfo, TypeRegistration, Typed, + self as bevy_reflect, Array, ArrayIter, FromReflect, FromType, GetTypeRegistration, List, + ListInfo, Reflect, ReflectFromPtr, ReflectMut, ReflectRef, TypeInfo, TypePath, + TypeRegistration, Typed, }; -impl Array for SmallVec +impl_type_path!(SmallVec); + +impl Array for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn get(&self, index: usize) -> Option<&dyn Reflect> { if index < SmallVec::len(self) { @@ -45,16 +49,16 @@ where } } -impl List for SmallVec +impl List for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn push(&mut self, value: Box) { let value = value.take::().unwrap_or_else(|value| { ::Item::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to push invalid value of type {}.", - value.type_name() + value.type_path() ) }) }); @@ -66,12 +70,13 @@ where } } -impl Reflect for SmallVec +impl Reflect for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { - fn type_name(&self) -> &str { - std::any::type_name::() + #[inline] + fn type_path(&self) -> &str { + ::type_path() } fn get_type_info(&self) -> &'static TypeInfo { @@ -124,9 +129,9 @@ where } } -impl Typed for SmallVec +impl Typed for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); @@ -134,9 +139,9 @@ where } } -impl FromReflect for SmallVec +impl FromReflect for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::List(ref_list) = reflect.reflect_ref() { @@ -151,9 +156,9 @@ where } } -impl GetTypeRegistration for SmallVec +impl GetTypeRegistration for SmallVec where - T::Item: FromReflect, + T::Item: FromReflect + TypePath, { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 6767c9d1c49b1..f2155d0804545 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,4 +1,4 @@ -use crate::{self as bevy_reflect, ReflectFromPtr}; +use crate::{self as bevy_reflect, ReflectFromPtr, TypePath}; use crate::{ map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum, EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, @@ -7,8 +7,8 @@ use crate::{ VariantInfo, VariantType, }; -use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; -use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; +use crate::utility::{GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell}; +use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value, impl_type_path}; use bevy_utils::{Duration, Instant}; use bevy_utils::{HashMap, HashSet}; #[cfg(any(unix, windows))] @@ -42,34 +42,37 @@ impl_reflect_value!(isize(Debug, Hash, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize)); impl_reflect_value!(String(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(PathBuf(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(Result()); -impl_reflect_value!(HashSet()); -impl_reflect_value!(Range()); -impl_reflect_value!(RangeInclusive()); -impl_reflect_value!(RangeFrom()); -impl_reflect_value!(RangeTo()); -impl_reflect_value!(RangeToInclusive()); -impl_reflect_value!(RangeFull()); -impl_reflect_value!(Duration(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(Instant(Debug, Hash, PartialEq)); -impl_reflect_value!(NonZeroI128(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU128(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroIsize(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroUsize(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI64(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU64(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU32(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI32(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI16(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU16(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU8(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI8(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" PathBuf(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!( + Result < T: Clone + Reflect + TypePath, + E: Clone + Reflect + TypePath > () +); +impl_reflect_value!(HashSet()); +impl_reflect_value!(@"std" Range()); +impl_reflect_value!(@"std" RangeInclusive()); +impl_reflect_value!(@"std" RangeFrom()); +impl_reflect_value!(@"std" RangeTo()); +impl_reflect_value!(@"std" RangeToInclusive()); +impl_reflect_value!(@"std" RangeFull()); +impl_reflect_value!(@"std" Duration(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" Instant(Debug, Hash, PartialEq)); +impl_reflect_value!(@"std" NonZeroI128(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroU128(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroIsize(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroUsize(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroI64(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroU64(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroU32(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroI32(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroI16(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroU16(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroU8(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" NonZeroI8(Debug, Hash, PartialEq, Serialize, Deserialize)); // Only for platforms that can serialize it as in serde: // https://github.com/serde-rs/serde/blob/3ffb86fc70efd3d329519e2dddfa306cc04f167c/serde/src/de/impls.rs#L1732 #[cfg(any(unix, windows))] -impl_reflect_value!(OsString(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(@"std" OsString(Debug, Hash, PartialEq, Serialize, Deserialize)); impl_from_reflect_value!(bool); impl_from_reflect_value!(char); @@ -88,12 +91,12 @@ impl_from_reflect_value!(isize); impl_from_reflect_value!(f32); impl_from_reflect_value!(f64); impl_from_reflect_value!(String); -impl_from_reflect_value!(HashSet); -impl_from_reflect_value!(Range); -impl_from_reflect_value!(RangeInclusive); -impl_from_reflect_value!(RangeFrom); -impl_from_reflect_value!(RangeTo); -impl_from_reflect_value!(RangeToInclusive); +impl_from_reflect_value!(HashSet); +impl_from_reflect_value!(Range); +impl_from_reflect_value!(RangeInclusive); +impl_from_reflect_value!(RangeFrom); +impl_from_reflect_value!(RangeTo); +impl_from_reflect_value!(RangeToInclusive); impl_from_reflect_value!(RangeFull); impl_from_reflect_value!(Duration); impl_from_reflect_value!(Instant); @@ -110,7 +113,66 @@ impl_from_reflect_value!(NonZeroU16); impl_from_reflect_value!(NonZeroU8); impl_from_reflect_value!(NonZeroI8); -impl Array for Vec { +impl_type_path!(Vec); +impl_type_path!(HashMap); +impl_type_path!(Option); + +// impl_type_name expects a type path followed by generic between `<` and `>`. +// so array is manually implemented. +impl TypePath for [T; N] { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("[{}; {N}]", T::type_path())) + } + #[inline] + fn short_type_name_base() -> &'static str { + ::type_path() + } + + #[inline] + fn short_type_name() -> &'static str { + ::type_path() + } + + #[inline] + fn module_path() -> &'static str { + "" + } + + #[inline] + fn crate_name() -> &'static str { + "" + } +} + +impl TypePath for Cow<'static, str> { + #[inline] + fn type_path() -> &'static str { + "Cow<'static, str>" + } + + #[inline] + fn short_type_name_base() -> &'static str { + "Cow" + } + + #[inline] + fn short_type_name() -> &'static str { + ::type_path() + } + + #[inline] + fn module_path() -> &'static str { + "" + } + + #[inline] + fn crate_name() -> &'static str { + "" + } +} + +impl Array for Vec { #[inline] fn get(&self, index: usize) -> Option<&dyn Reflect> { <[T]>::get(self, index).map(|value| value as &dyn Reflect) @@ -142,13 +204,13 @@ impl Array for Vec { } } -impl List for Vec { +impl List for Vec { fn push(&mut self, value: Box) { let value = value.take::().unwrap_or_else(|value| { T::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to push invalid value of type {}.", - value.type_name() + value.type_path() ) }) }); @@ -160,9 +222,9 @@ impl List for Vec { } } -impl Reflect for Vec { - fn type_name(&self) -> &str { - std::any::type_name::() +impl Reflect for Vec { + fn type_path(&self) -> &str { + ::type_path() } fn get_type_info(&self) -> &'static TypeInfo { @@ -219,14 +281,14 @@ impl Reflect for Vec { } } -impl Typed for Vec { +impl Typed for Vec { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| TypeInfo::List(ListInfo::new::())) } } -impl GetTypeRegistration for Vec { +impl GetTypeRegistration for Vec { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); registration.insert::(FromType::>::from_type()); @@ -234,7 +296,7 @@ impl GetTypeRegistration for Vec { } } -impl FromReflect for Vec { +impl FromReflect for Vec { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::List(ref_list) = reflect.reflect_ref() { let mut new_list = Self::with_capacity(ref_list.len()); @@ -248,7 +310,7 @@ impl FromReflect for Vec { } } -impl Map for HashMap { +impl Map for HashMap { fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> { key.downcast_ref::() .and_then(|key| HashMap::get(self, key)) @@ -291,7 +353,7 @@ impl Map for HashMap { fn clone_dynamic(&self) -> DynamicMap { let mut dynamic_map = DynamicMap::default(); - dynamic_map.set_name(self.type_name().to_string()); + dynamic_map.set_name(self.type_path().to_string()); for (k, v) in self { dynamic_map.insert_boxed(k.clone_value(), v.clone_value()); } @@ -307,7 +369,7 @@ impl Map for HashMap { K::from_reflect(&*key).unwrap_or_else(|| { panic!( "Attempted to insert invalid key of type {}.", - key.type_name() + key.type_path() ) }) }); @@ -315,7 +377,7 @@ impl Map for HashMap { V::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to insert invalid value of type {}.", - value.type_name() + value.type_path() ) }) }); @@ -324,9 +386,9 @@ impl Map for HashMap { } } -impl Reflect for HashMap { - fn type_name(&self) -> &str { - std::any::type_name::() +impl Reflect for HashMap { + fn type_path(&self) -> &str { + ::type_path() } fn get_type_info(&self) -> &'static TypeInfo { @@ -379,7 +441,7 @@ impl Reflect for HashMap { } } -impl Typed for HashMap { +impl Typed for HashMap { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| TypeInfo::Map(MapInfo::new::())) @@ -388,8 +450,8 @@ impl Typed for HashMap { impl GetTypeRegistration for HashMap where - K: FromReflect + Eq + Hash, - V: FromReflect, + K: FromReflect + TypePath + Eq + Hash, + V: FromReflect + TypePath, { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); @@ -398,7 +460,9 @@ where } } -impl FromReflect for HashMap { +impl FromReflect + for HashMap +{ fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Map(ref_map) = reflect.reflect_ref() { let mut new_map = Self::with_capacity(ref_map.len()); @@ -414,7 +478,7 @@ impl FromReflect for HashMap { } } -impl Array for [T; N] { +impl Array for [T; N] { #[inline] fn get(&self, index: usize) -> Option<&dyn Reflect> { <[T]>::get(self, index).map(|value| value as &dyn Reflect) @@ -446,10 +510,9 @@ impl Array for [T; N] { } } -impl Reflect for [T; N] { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() +impl Reflect for [T; N] { + fn type_path(&self) -> &str { + ::type_path() } fn get_type_info(&self) -> &'static TypeInfo { @@ -518,7 +581,7 @@ impl Reflect for [T; N] { } } -impl FromReflect for [T; N] { +impl FromReflect for [T; N] { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Array(ref_array) = reflect.reflect_ref() { let mut temp_vec = Vec::with_capacity(ref_array.len()); @@ -532,7 +595,7 @@ impl FromReflect for [T; N] { } } -impl Typed for [T; N] { +impl Typed for [T; N] { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| TypeInfo::Array(ArrayInfo::new::(N))) @@ -547,7 +610,7 @@ impl Typed for [T; N] { macro_rules! impl_array_get_type_registration { ($($N:expr)+) => { $( - impl GetTypeRegistration for [T; $N] { + impl GetTypeRegistration for [T; $N] { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::<[T; $N]>() } @@ -564,8 +627,8 @@ impl_array_get_type_registration! { } impl Reflect for Cow<'static, str> { - fn type_name(&self) -> &str { - std::any::type_name::() + fn type_path(&self) -> &str { + ::type_path() } fn get_type_info(&self) -> &'static TypeInfo { @@ -597,7 +660,7 @@ impl Reflect for Cow<'static, str> { if let Some(value) = value.downcast_ref::() { *self = value.clone(); } else { - panic!("Value is not a {}.", std::any::type_name::()); + panic!("Value is not a {}.", ::type_path()); } } @@ -635,13 +698,13 @@ impl Reflect for Cow<'static, str> { } } -impl GetTypeRegistration for Option { +impl GetTypeRegistration for Option { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::>() } } -impl Enum for Option { +impl Enum for Option { fn field(&self, _name: &str) -> Option<&dyn Reflect> { None } @@ -712,10 +775,9 @@ impl Enum for Option { } } -impl Reflect for Option { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() +impl Reflect for Option { + fn type_path(&self) -> &str { + ::type_path() } #[inline] @@ -765,7 +827,7 @@ impl Reflect for Option { .unwrap_or_else(|| { panic!( "Field in `Some` variant of {} should exist", - std::any::type_name::>() + as TypePath>::type_path() ) }) .clone_value() @@ -774,8 +836,8 @@ impl Reflect for Option { T::from_reflect(&*value).unwrap_or_else(|| { panic!( "Field in `Some` variant of {} should be of type {}", - std::any::type_name::>(), - std::any::type_name::() + as TypePath>::type_path(), + ::type_path() ) }) }); @@ -784,7 +846,7 @@ impl Reflect for Option { "None" => { *self = None; } - _ => panic!("Enum is not a {}.", std::any::type_name::()), + _ => panic!("Enum is not a {}.", ::type_path()), } } } @@ -818,7 +880,7 @@ impl Reflect for Option { } } -impl FromReflect for Option { +impl FromReflect for Option { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() { match dyn_enum.variant_name() { @@ -828,7 +890,7 @@ impl FromReflect for Option { .unwrap_or_else(|| { panic!( "Field in `Some` variant of {} should exist", - std::any::type_name::>() + as TypePath>::type_path() ) }) .clone_value() @@ -837,8 +899,8 @@ impl FromReflect for Option { T::from_reflect(&*value).unwrap_or_else(|| { panic!( "Field in `Some` variant of {} should be of type {}", - std::any::type_name::>(), - std::any::type_name::() + as TypePath>::type_path(), + ::type_path() ) }) }); @@ -848,7 +910,7 @@ impl FromReflect for Option { name => panic!( "variant with name `{}` does not exist on enum `{}`", name, - std::any::type_name::() + ::type_path() ), } } else { @@ -857,7 +919,7 @@ impl FromReflect for Option { } } -impl Typed for Option { +impl Typed for Option { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { @@ -1001,7 +1063,7 @@ mod tests { .unwrap_or_default()); assert_eq!("Some", value.variant_name()); - assert_eq!("core::option::Option::Some", value.variant_path()); + assert_eq!("Option::Some", value.variant_path()); if value.is_variant(VariantType::Tuple) { if let Some(field) = value diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index a9e57d3fbfac0..c89a17e4c3c10 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -10,6 +10,7 @@ mod struct_trait; mod tuple; mod tuple_struct; mod type_info; +mod type_path; mod type_registry; mod type_uuid; mod impls { @@ -40,7 +41,7 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ reflect_trait, FromReflect, GetField, GetTupleStructField, Reflect, ReflectDeserialize, - ReflectSerialize, Struct, TupleStruct, + ReflectSerialize, Struct, TupleStruct, TypePath, }; } @@ -56,6 +57,7 @@ pub use struct_trait::*; pub use tuple::*; pub use tuple_struct::*; pub use type_info::*; +pub use type_path::*; pub use type_registry::*; pub use type_uuid::*; @@ -569,25 +571,25 @@ mod tests { fn dynamic_names() { let list = Vec::::new(); let dyn_list = List::clone_dynamic(&list); - assert_eq!(dyn_list.type_name(), std::any::type_name::>()); + assert_eq!(dyn_list.type_path(), as TypePath>::type_path()); let array = [b'0'; 4]; let dyn_array = Array::clone_dynamic(&array); - assert_eq!(dyn_array.type_name(), std::any::type_name::<[u8; 4]>()); + assert_eq!(dyn_array.type_path(), <[u8; 4] as TypePath>::type_path()); let map = HashMap::::default(); let dyn_map = map.clone_dynamic(); assert_eq!( - dyn_map.type_name(), - std::any::type_name::>() + dyn_map.type_path(), + as TypePath>::type_path() ); let tuple = (0usize, "1".to_string(), 2.0f32); let mut dyn_tuple = tuple.clone_dynamic(); dyn_tuple.insert::(3); assert_eq!( - dyn_tuple.type_name(), - std::any::type_name::<(usize, String, f32, usize)>() + dyn_tuple.type_path(), + <(usize, String, f32, usize) as TypePath>::type_path() ); #[derive(Reflect)] @@ -596,15 +598,18 @@ mod tests { } let struct_ = TestStruct { a: 0 }; let dyn_struct = struct_.clone_dynamic(); - assert_eq!(dyn_struct.type_name(), std::any::type_name::()); + assert_eq!( + dyn_struct.type_path(), + ::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::() + dyn_tuple_struct.type_path(), + ::type_path() ); } @@ -612,7 +617,7 @@ mod tests { fn reflect_type_info() { // TypeInfo let info = i32::type_info(); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(type_path::(), info.type_path()); assert_eq!(std::any::TypeId::of::(), info.type_id()); // TypeInfo (unsized) @@ -636,21 +641,15 @@ mod tests { let info = MyStruct::type_info(); if let TypeInfo::Struct(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!( - std::any::type_name::(), - info.field("foo").unwrap().type_name() - ); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.field("foo").unwrap().type_path()); assert_eq!( std::any::TypeId::of::(), info.field("foo").unwrap().type_id() ); assert!(info.field("foo").unwrap().is::()); assert_eq!("foo", info.field("foo").unwrap().name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(type_path::(), info.field_at(1).unwrap().type_path()); } else { panic!("Expected `TypeInfo::Struct`"); } @@ -661,7 +660,7 @@ mod tests { // Struct (generic) #[derive(Reflect)] - struct MyGenericStruct { + struct MyGenericStruct { foo: T, bar: usize, } @@ -669,19 +668,10 @@ mod tests { let info = >::type_info(); if let TypeInfo::Struct(info) = info { assert!(info.is::>()); - assert_eq!( - std::any::type_name::>(), - info.type_name() - ); - assert_eq!( - std::any::type_name::(), - info.field("foo").unwrap().type_name() - ); + assert_eq!(type_path::>(), info.type_path()); + assert_eq!(type_path::(), info.field("foo").unwrap().type_path()); assert_eq!("foo", info.field("foo").unwrap().name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(type_path::(), info.field_at(1).unwrap().type_path()); } else { panic!("Expected `TypeInfo::Struct`"); } @@ -700,11 +690,8 @@ mod tests { let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.field_at(1).unwrap().type_path()); assert!(info.field_at(1).unwrap().is::()); } else { panic!("Expected `TypeInfo::TupleStruct`"); @@ -716,11 +703,8 @@ mod tests { let info = MyTuple::type_info(); if let TypeInfo::Tuple(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!( - std::any::type_name::(), - info.field_at(1).unwrap().type_name() - ); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.field_at(1).unwrap().type_path()); } else { panic!("Expected `TypeInfo::Tuple`"); } @@ -736,8 +720,8 @@ mod tests { if let TypeInfo::List(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.item_type_path()); } else { panic!("Expected `TypeInfo::List`"); } @@ -755,8 +739,8 @@ mod tests { if let TypeInfo::List(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.item_type_path()); } else { panic!("Expected `TypeInfo::List`"); } @@ -774,8 +758,8 @@ mod tests { if let TypeInfo::Array(info) = info { assert!(info.is::()); assert!(info.item_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.item_type_name()); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.item_type_path()); assert_eq!(3, info.capacity()); } else { panic!("Expected `TypeInfo::Array`"); @@ -793,9 +777,9 @@ mod tests { assert!(info.is::()); assert!(info.key_is::()); assert!(info.value_is::()); - assert_eq!(std::any::type_name::(), info.type_name()); - assert_eq!(std::any::type_name::(), info.key_type_name()); - assert_eq!(std::any::type_name::(), info.value_type_name()); + assert_eq!(type_path::(), info.type_path()); + assert_eq!(type_path::(), info.key_type_path()); + assert_eq!(type_path::(), info.value_type_path()); } else { panic!("Expected `TypeInfo::Map`"); } @@ -810,7 +794,7 @@ mod tests { let info = MyValue::type_info(); if let TypeInfo::Value(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(type_path::(), info.type_path()); } else { panic!("Expected `TypeInfo::Value`"); } @@ -825,7 +809,7 @@ mod tests { let info = MyDynamic::type_info(); if let TypeInfo::Dynamic(info) = info { assert!(info.is::()); - assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(::type_path(), info.type_path()); } else { panic!("Expected `TypeInfo::Dynamic`"); } @@ -914,7 +898,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", @@ -929,10 +913,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, @@ -948,6 +932,38 @@ bevy_reflect::tests::should_reflect_debug::Test { assert_eq!(expected, format!("\n{:#?}", reflected)); } + #[test] + fn reflect_type_name() { + #[derive(TypePath)] + struct Foo; + let name = Foo::type_path(); + assert_eq!(name, "bevy_reflect::tests::Foo"); + + #[derive(TypePath)] + struct Goo { + _value: T, + } + let name = Goo::::type_path(); + assert_eq!(name, "bevy_reflect::tests::Goo"); + } + + #[test] + fn reflect_custom_type_path() { + #[derive(TypePath)] + #[type_path(path = "", ident = "Banane")] + struct Foo; + let name = Foo::type_path(); + assert_eq!(name, "Banane"); + + #[derive(TypePath)] + #[type_path(path = "", ident = "MyType")] + struct Goo { + _value: T, + } + let name = Goo::::type_path(); + assert_eq!(name, "MyType"); + } + #[cfg(feature = "glam")] mod glam { use super::*; @@ -968,7 +984,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, @@ -982,7 +998,7 @@ bevy_reflect::tests::should_reflect_debug::Test { fn vec3_deserialization() { let data = r#" { - "glam::f32::vec3::Vec3": ( + "glam::Vec3": ( x: 12.0, y: 3.0, z: -6.9, diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index b832e80c37e2a..ac5b16df46488 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -3,8 +3,8 @@ use std::fmt::{Debug, Formatter}; use crate::utility::NonGenericTypeInfoCell; use crate::{ - Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectRef, - TypeInfo, Typed, + self as bevy_reflect, type_path, Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, + Reflect, ReflectMut, ReflectRef, TypeInfo, TypePath, Typed, }; /// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`]. @@ -21,7 +21,7 @@ pub trait List: Reflect + Array { /// Clones the list, producing a [`DynamicList`]. fn clone_dynamic(&self) -> DynamicList { DynamicList { - name: self.type_name().to_string(), + name: self.type_path().to_string(), values: self.iter().map(|value| value.clone_value()).collect(), } } @@ -30,28 +30,28 @@ pub trait List: Reflect + Array { /// A container for compile-time list info. #[derive(Clone, Debug)] pub struct ListInfo { - type_name: &'static str, + type_path: &'static str, type_id: TypeId, - item_type_name: &'static str, + item_type_path: &'static str, item_type_id: TypeId, } impl ListInfo { /// Create a new [`ListInfo`]. - pub fn new() -> Self { + pub fn new() -> Self { Self { - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), - item_type_name: std::any::type_name::(), + item_type_path: type_path::(), item_type_id: TypeId::of::(), } } - /// The [type name] of the list. + /// The [type path] of the list. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the list. @@ -64,11 +64,11 @@ impl ListInfo { TypeId::of::() == self.type_id } - /// The [type name] of the list item. + /// 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 + /// [type path]: TypePath + pub fn item_type_path(&self) -> &'static str { + self.item_type_path } /// The [`TypeId`] of the list item. @@ -83,25 +83,25 @@ impl ListInfo { } /// A list of reflected values. -#[derive(Default)] +#[derive(Default, TypePath)] pub struct DynamicList { name: String, values: Vec>, } impl DynamicList { - /// Returns the type name of the list. + /// Returns the type path of the list. /// /// The value returned by this method is the same value returned by - /// [`Reflect::type_name`]. + /// [`Reflect::type_path`]. pub fn name(&self) -> &str { &self.name } - /// Sets the type name of the list. + /// Sets the type path of the list. /// /// The value set by this method is the value returned by - /// [`Reflect::type_name`]. + /// [`Reflect::type_path`]. pub fn set_name(&mut self, name: String) { self.name = name; } @@ -176,8 +176,8 @@ impl List for DynamicList { impl Reflect for DynamicList { #[inline] - fn type_name(&self) -> &str { - self.name.as_str() + fn type_path(&self) -> &str { + &self.name } #[inline] diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 9bdeea87f61cb..1ffc46a4a6445 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -5,7 +5,10 @@ use std::hash::Hash; use bevy_utils::{Entry, HashMap}; use crate::utility::NonGenericTypeInfoCell; -use crate::{DynamicInfo, Reflect, ReflectMut, ReflectRef, TypeInfo, Typed}; +use crate::{ + self as bevy_reflect, type_path, DynamicInfo, Reflect, ReflectMut, ReflectRef, TypeInfo, + TypePath, Typed, +}; /// An ordered mapping between [`Reflect`] values. /// @@ -62,32 +65,36 @@ pub trait Map: Reflect { /// A container for compile-time map info. #[derive(Clone, Debug)] pub struct MapInfo { - type_name: &'static str, + type_path: &'static str, type_id: TypeId, - key_type_name: &'static str, + key_type_path: &'static str, key_type_id: TypeId, - value_type_name: &'static str, + value_type_path: &'static str, value_type_id: TypeId, } impl MapInfo { /// Create a new [`MapInfo`]. - pub fn new() -> Self { + pub fn new< + TMap: Map + TypePath, + TKey: Hash + Reflect + TypePath, + TValue: Reflect + TypePath, + >() -> Self { Self { - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), - key_type_name: std::any::type_name::(), + key_type_path: type_path::(), key_type_id: TypeId::of::(), - value_type_name: std::any::type_name::(), + value_type_path: type_path::(), value_type_id: TypeId::of::(), } } - /// The [type name] of the map. + /// The [type path] of the map. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the map. @@ -100,11 +107,11 @@ impl MapInfo { TypeId::of::() == self.type_id } - /// The [type name] of the key. + /// The [type path] of the key. /// - /// [type name]: std::any::type_name - pub fn key_type_name(&self) -> &'static str { - self.key_type_name + /// [type path]: TypePath + pub fn key_type_path(&self) -> &'static str { + self.key_type_path } /// The [`TypeId`] of the key. @@ -117,11 +124,11 @@ impl MapInfo { TypeId::of::() == self.key_type_id } - /// The [type name] of the value. + /// The [type path] of the value. /// - /// [type name]: std::any::type_name - pub fn value_type_name(&self) -> &'static str { - self.value_type_name + /// [type path]: TypePath + pub fn value_type_path(&self) -> &'static str { + self.value_type_path } /// The [`TypeId`] of the value. @@ -138,7 +145,7 @@ impl MapInfo { const HASH_ERROR: &str = "the given key does not support hashing"; /// An ordered mapping between reflected values. -#[derive(Default)] +#[derive(Default, TypePath)] pub struct DynamicMap { name: String, values: Vec<(Box, Box)>, @@ -146,18 +153,18 @@ pub struct DynamicMap { } impl DynamicMap { - /// Returns the type name of the map. + /// Returns the type path of the map. /// /// The value returned by this method is the same value returned by - /// [`Reflect::type_name`]. + /// [`Reflect::type_path`]. pub fn name(&self) -> &str { &self.name } - /// Sets the type name of the map. + /// Sets the type path of the map. /// /// The value set by this method is the same value returned by - /// [`Reflect::type_name`]. + /// [`Reflect::type_path`]. pub fn set_name(&mut self, name: String) { self.name = name; } @@ -236,7 +243,8 @@ impl Map for DynamicMap { } impl Reflect for DynamicMap { - fn type_name(&self) -> &str { + #[inline] + fn type_path(&self) -> &str { &self.name } diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index fb8285fd9de0c..fa8445d64277a 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,10 +1,10 @@ use crate::{ array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, - tuple_struct_debug, Array, Enum, List, Map, Struct, Tuple, TupleStruct, TypeInfo, Typed, - ValueInfo, + tuple_struct_debug, Array, Enum, List, Map, Struct, Tuple, TupleStruct, TypeInfo, TypePath, + Typed, ValueInfo, }; use std::{ - any::{self, Any, TypeId}, + any::{Any, TypeId}, fmt::Debug, }; @@ -53,8 +53,8 @@ pub enum ReflectMut<'a> { /// When using `#[derive(Reflect)]` on a struct, tuple struct or enum, the suitable subtrait for that /// type (`Struct`, `TupleStruct` or `Enum`) is derived automatically. pub trait Reflect: Any + Send + Sync { - /// Returns the [type name][std::any::type_name] of the underlying type. - fn type_name(&self) -> &str; + /// Returns the [type path][crate::TypePath] of the underlying type. + fn type_path(&self) -> &str; /// Returns the [`TypeInfo`] of the underlying type. /// @@ -165,10 +165,10 @@ pub trait Reflect: 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]: Self::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), @@ -178,7 +178,7 @@ pub trait Reflect: 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.type_path()), } } @@ -218,6 +218,33 @@ impl Typed for dyn Reflect { } } +impl TypePath for dyn Reflect { + #[inline] + fn type_path() -> &'static str { + "dyn Reflect" + } + + #[inline] + fn short_type_name_base() -> &'static str { + ::type_path() + } + + #[inline] + fn short_type_name() -> &'static str { + ::type_path() + } + + #[inline] + fn module_path() -> &'static str { + "" + } + + #[inline] + fn crate_name() -> &'static str { + "" + } +} + #[deny(rustdoc::broken_intra_doc_links)] impl dyn Reflect { /// Downcasts the value to type `T`, consuming the trait object. @@ -243,8 +270,8 @@ impl dyn Reflect { /// /// Read `is` for more information on underlying values and represented types. #[inline] - pub fn represents(&self) -> bool { - self.type_name() == any::type_name::() + pub fn represents(&self) -> bool { + self.type_path() == ::type_path() } /// Returns `true` if the underlying value is of type `T`, or `false` diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index d0f03d0f8631b..e08360afc46c4 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -37,7 +37,7 @@ trait TupleLikeInfo { impl StructLikeInfo for StructInfo { fn get_name(&self) -> &str { - self.type_name() + self.type_path() } fn get_field(&self, name: &str) -> Option<&NamedField> { @@ -65,7 +65,7 @@ impl StructLikeInfo for StructVariantInfo { impl TupleLikeInfo for TupleInfo { fn get_name(&self) -> &str { - self.type_name() + self.type_path() } fn get_field(&self, index: usize) -> Option<&UnnamedField> { @@ -267,7 +267,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_path(); // Handle both Value case and types that have a custom `ReflectDeserialize` if let Some(deserialize_reflect) = self.registration.data::() { @@ -286,7 +286,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { registry: self.registry, }, )?; - dynamic_struct.set_name(struct_info.type_name().to_string()); + dynamic_struct.set_name(struct_info.type_path().to_string()); Ok(Box::new(dynamic_struct)) } TypeInfo::TupleStruct(tuple_struct_info) => { @@ -299,7 +299,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { registration: self.registration, }, )?; - dynamic_tuple_struct.set_name(tuple_struct_info.type_name().to_string()); + dynamic_tuple_struct.set_name(tuple_struct_info.type_path().to_string()); Ok(Box::new(dynamic_tuple_struct)) } TypeInfo::List(list_info) => { @@ -307,7 +307,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { list_info, registry: self.registry, })?; - dynamic_list.set_name(list_info.type_name().to_string()); + dynamic_list.set_name(list_info.type_path().to_string()); Ok(Box::new(dynamic_list)) } TypeInfo::Array(array_info) => { @@ -318,7 +318,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { registry: self.registry, }, )?; - dynamic_array.set_name(array_info.type_name().to_string()); + dynamic_array.set_name(array_info.type_path().to_string()); Ok(Box::new(dynamic_array)) } TypeInfo::Map(map_info) => { @@ -326,7 +326,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { map_info, registry: self.registry, })?; - dynamic_map.set_name(map_info.type_name().to_string()); + dynamic_map.set_name(map_info.type_path().to_string()); Ok(Box::new(dynamic_map)) } TypeInfo::Tuple(tuple_info) => { @@ -337,12 +337,12 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { registry: self.registry, }, )?; - dynamic_tuple.set_name(tuple_info.type_name().to_string()); + dynamic_tuple.set_name(tuple_info.type_path().to_string()); 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 type_path = enum_info.type_path(); + let mut dynamic_enum = if type_path.starts_with("Option<") { deserializer.deserialize_option(OptionVisitor { enum_info, registry: self.registry, @@ -358,14 +358,14 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { }, )? }; - dynamic_enum.set_name(type_name.to_string()); + dynamic_enum.set_name(type_path.to_string()); Ok(Box::new(dynamic_enum)) } TypeInfo::Value(_) => { // This case should already be handled Err(de::Error::custom(format_args!( "the TypeRegistration for {} doesn't have ReflectDeserialize", - type_name + type_path ))) } TypeInfo::Dynamic(_) => { @@ -373,7 +373,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { // fields are and would rely on the deserializer to determine them (e.g. `i32` vs `i64`) Err(de::Error::custom(format_args!( "cannot deserialize arbitrary dynamic type {}", - type_name + type_path ))) } } @@ -425,10 +425,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 { @@ -497,7 +497,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(), self.registry, )?; while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { @@ -537,7 +537,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(), self.registry, )?; while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { @@ -569,12 +569,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(), 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(), self.registry, )?; while let Some(key) = map.next_key_seed(TypedReflectDeserializer { @@ -633,7 +633,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, @@ -708,7 +708,7 @@ impl<'a, 'de> Visitor<'de> for OptionVisitor<'a> { fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { formatter.write_str("reflected option value of type ")?; - formatter.write_str(self.enum_info.type_name()) + formatter.write_str(self.enum_info.type_path()) } fn visit_some(self, deserializer: D) -> Result @@ -720,7 +720,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, @@ -767,7 +767,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, @@ -794,7 +794,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 { @@ -822,13 +822,13 @@ 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 + type_path )) })?; Ok(registration) @@ -1064,7 +1064,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(( @@ -1086,7 +1086,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: ( @@ -1123,7 +1123,7 @@ mod tests { // === Unit Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": Unit, + "bevy_reflect::serde::de::tests::MyEnum": Unit, }"#; let reflect_deserializer = UntypedReflectDeserializer::new(®istry); let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); @@ -1134,7 +1134,7 @@ mod tests { // === NewType Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": NewType(123), + "bevy_reflect::serde::de::tests::MyEnum": NewType(123), }"#; let reflect_deserializer = UntypedReflectDeserializer::new(®istry); let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); @@ -1145,7 +1145,7 @@ mod tests { // === Tuple Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": Tuple(1.23, 3.21), + "bevy_reflect::serde::de::tests::MyEnum": Tuple(1.23, 3.21), }"#; let reflect_deserializer = UntypedReflectDeserializer::new(®istry); let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); @@ -1156,7 +1156,7 @@ mod tests { // === Struct Variant === // let input = r#"{ - "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum": Struct( + "bevy_reflect::serde::de::tests::MyEnum": Struct( value: "I <3 Enums", ), }"#; diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index 8425f1e0a015b..a8637ab2bcae5 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -37,7 +37,7 @@ fn get_serializable<'a, E: serde::ser::Error>( .ok_or_else(|| { serde::ser::Error::custom(format_args!( "Type '{}' did not register ReflectSerialize", - reflect_value.type_name() + reflect_value.type_path() )) })?; Ok(reflect_serialize.get_serializable(reflect_value)) @@ -89,7 +89,7 @@ impl<'a> Serialize for ReflectSerializer<'a> { { let mut state = serializer.serialize_map(Some(1))?; state.serialize_entry( - self.value.type_name(), + self.value.type_path(), &TypedReflectSerializer::new(self.value, self.registry), )?; state.end() @@ -189,7 +189,7 @@ impl<'a> Serialize for StructSerializer<'a> { { let type_info = get_type_info( self.struct_value.get_type_info(), - self.struct_value.type_name(), + self.struct_value.type_path(), self.registry, )?; @@ -239,7 +239,7 @@ impl<'a> Serialize for TupleStructSerializer<'a> { { let type_info = get_type_info( self.tuple_struct.get_type_info(), - self.tuple_struct.type_name(), + self.tuple_struct.type_path(), self.registry, )?; @@ -288,7 +288,7 @@ impl<'a> Serialize for EnumSerializer<'a> { { let type_info = get_type_info( self.enum_value.get_type_info(), - self.enum_value.type_name(), + self.enum_value.type_path(), self.registry, )?; @@ -318,11 +318,7 @@ 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.type_path().starts_with("Option<") { serializer.serialize_none() } else { serializer.serialize_unit_variant(enum_name, variant_index, variant_name) @@ -356,11 +352,7 @@ impl<'a> Serialize for EnumSerializer<'a> { } VariantType::Tuple if field_len == 1 => { let field = self.enum_value.field_at(0).unwrap(); - if self - .enum_value - .type_name() - .starts_with("core::option::Option") - { + if self.enum_value.type_path().starts_with("Option<") { serializer.serialize_some(&TypedReflectSerializer::new(field, self.registry)) } else { serializer.serialize_newtype_variant( @@ -641,7 +633,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(( @@ -661,7 +653,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: ( @@ -693,7 +685,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": Unit, + "bevy_reflect::serde::ser::tests::MyEnum": Unit, }"#; assert_eq!(expected, output); @@ -702,7 +694,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": NewType(123), + "bevy_reflect::serde::ser::tests::MyEnum": NewType(123), }"#; assert_eq!(expected, output); @@ -711,7 +703,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": Tuple(1.23, 3.21), + "bevy_reflect::serde::ser::tests::MyEnum": Tuple(1.23, 3.21), }"#; assert_eq!(expected, output); @@ -722,7 +714,7 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); let expected = r#"{ - "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum": Struct( + "bevy_reflect::serde::ser::tests::MyEnum": Struct( value: "I <3 Enums", ), }"#; diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index f246b7128cc35..e10c30574e251 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,5 +1,8 @@ use crate::utility::NonGenericTypeInfoCell; -use crate::{DynamicInfo, NamedField, Reflect, ReflectMut, ReflectRef, TypeInfo, Typed}; +use crate::{ + self as bevy_reflect, type_path, DynamicInfo, NamedField, Reflect, ReflectMut, ReflectRef, + TypeInfo, TypePath, Typed, +}; use bevy_utils::{Entry, HashMap}; use std::fmt::{Debug, Formatter}; use std::{ @@ -70,7 +73,7 @@ pub trait Struct: Reflect { #[derive(Clone, Debug)] pub struct StructInfo { name: &'static str, - type_name: &'static str, + type_path: &'static str, type_id: TypeId, fields: Box<[NamedField]>, field_indices: HashMap<&'static str, usize>, @@ -84,7 +87,7 @@ impl StructInfo { /// * `name`: The name of this struct (_without_ generics or lifetimes) /// * `fields`: The fields of this struct in the order they are defined /// - pub fn new(name: &'static str, fields: &[NamedField]) -> Self { + pub fn new(name: &'static str, fields: &[NamedField]) -> Self { let field_indices = fields .iter() .enumerate() @@ -93,7 +96,7 @@ impl StructInfo { Self { name, - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), field_indices, @@ -138,9 +141,9 @@ impl StructInfo { /// The [type name] of the struct. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the struct. @@ -239,7 +242,7 @@ impl GetField for dyn Struct { } /// A struct type which allows fields to be added at runtime. -#[derive(Default)] +#[derive(Default, TypePath)] pub struct DynamicStruct { name: String, fields: Vec>, @@ -248,12 +251,12 @@ pub struct DynamicStruct { } impl DynamicStruct { - /// Returns the type name of the struct. + /// Returns the type path of the struct. pub fn name(&self) -> &str { &self.name } - /// Sets the type name of the struct. + /// Sets the type path of the struct. pub fn set_name(&mut self, name: String) { self.name = name; } @@ -353,7 +356,7 @@ impl Struct for DynamicStruct { impl Reflect for DynamicStruct { #[inline] - fn type_name(&self) -> &str { + fn type_path(&self) -> &str { &self.name } @@ -501,7 +504,7 @@ pub fn struct_partial_eq(a: &S, b: &dyn Reflect) -> Option { /// ``` #[inline] pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut debug = f.debug_struct(dyn_struct.type_name()); + let mut debug = f.debug_struct(dyn_struct.type_path()); for field_index in 0..dyn_struct.field_len() { let field = dyn_struct.field_at(field_index).unwrap(); debug.field( diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index b31b5f78e8da7..d9937f4c93732 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -1,7 +1,7 @@ use crate::utility::NonGenericTypeInfoCell; use crate::{ - DynamicInfo, FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectRef, TypeInfo, - TypeRegistration, Typed, UnnamedField, + self as bevy_reflect, type_path, DynamicInfo, FromReflect, GetTypeRegistration, Reflect, + ReflectMut, ReflectRef, TypeInfo, TypePath, TypeRegistration, Typed, UnnamedField, }; use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; @@ -131,7 +131,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: &'static str, type_id: TypeId, fields: Box<[UnnamedField]>, } @@ -143,9 +143,9 @@ impl TupleInfo { /// /// * `fields`: The fields of this tuple in the order they are defined /// - pub fn new(fields: &[UnnamedField]) -> Self { + pub fn new(fields: &[UnnamedField]) -> Self { Self { - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), } @@ -166,11 +166,11 @@ impl TupleInfo { self.fields.len() } - /// The [type name] of the tuple. + /// The [type path] of the tuple. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the tuple. @@ -185,21 +185,21 @@ impl TupleInfo { } /// A tuple which allows fields to be added at runtime. -#[derive(Default, Debug)] +#[derive(Default, Debug, TypePath)] pub struct DynamicTuple { name: String, fields: Vec>, } impl DynamicTuple { - /// Returns the type name of the tuple. + /// Returns the type path of the tuple. /// /// The tuple's name is automatically generated from its element types. pub fn name(&self) -> &str { &self.name } - /// Manually sets the type name of the tuple. + /// Manually sets the type path of the tuple. /// /// Note that the tuple name will be overwritten when elements are added. pub fn set_name(&mut self, name: String) { @@ -226,7 +226,7 @@ impl DynamicTuple { if i > 0 { name.push_str(", "); } - name.push_str(field.type_name()); + name.push_str(field.type_path()); } name.push(')'); } @@ -276,8 +276,8 @@ impl Tuple for DynamicTuple { impl Reflect for DynamicTuple { #[inline] - fn type_name(&self) -> &str { - self.name() + fn type_path(&self) -> &str { + &self.name } #[inline] @@ -428,7 +428,7 @@ pub fn tuple_debug(dyn_tuple: &dyn Tuple, f: &mut std::fmt::Formatter<'_>) -> st macro_rules! impl_reflect_tuple { {$($index:tt : $name:tt),*} => { - impl<$($name: Reflect),*> Tuple for ($($name,)*) { + impl<$($name: Reflect + TypePath),*> Tuple for ($($name,)*) { #[inline] fn field(&self, index: usize) -> Option<&dyn Reflect> { match index { @@ -480,9 +480,10 @@ macro_rules! impl_reflect_tuple { } } - impl<$($name: Reflect),*> Reflect for ($($name,)*) { - fn type_name(&self) -> &str { - std::any::type_name::() + impl<$($name: Reflect + TypePath),*> Reflect for ($($name,)*) { + #[inline] + fn type_path(&self) -> &str { + ::type_path() } fn get_type_info(&self) -> &'static TypeInfo { @@ -535,7 +536,7 @@ macro_rules! impl_reflect_tuple { } } - impl <$($name: Reflect),*> Typed for ($($name,)*) { + impl <$($name: Reflect + TypePath),*> Typed for ($($name,)*) { fn type_info() -> &'static TypeInfo { static CELL: $crate::utility::GenericTypeInfoCell = $crate::utility::GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { @@ -548,13 +549,13 @@ macro_rules! impl_reflect_tuple { } } - impl<$($name: Reflect + Typed),*> GetTypeRegistration for ($($name,)*) { + impl<$($name: Reflect + TypePath + Typed),*> GetTypeRegistration for ($($name,)*) { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::<($($name,)*)>() } } - impl<$($name: FromReflect),*> FromReflect for ($($name,)*) + impl<$($name: FromReflect + TypePath),*> FromReflect for ($($name,)*) { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() { diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index e7bb89cebb5a8..1e9af7238c8f2 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,5 +1,8 @@ use crate::utility::NonGenericTypeInfoCell; -use crate::{DynamicInfo, Reflect, ReflectMut, ReflectRef, TypeInfo, Typed, UnnamedField}; +use crate::{ + self as bevy_reflect, type_path, DynamicInfo, Reflect, ReflectMut, ReflectRef, TypeInfo, + TypePath, Typed, UnnamedField, +}; use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; use std::slice::Iter; @@ -50,7 +53,7 @@ pub trait TupleStruct: Reflect { #[derive(Clone, Debug)] pub struct TupleStructInfo { name: &'static str, - type_name: &'static str, + type_path: &'static str, type_id: TypeId, fields: Box<[UnnamedField]>, } @@ -63,10 +66,10 @@ impl TupleStructInfo { /// * `name`: The name of this struct (_without_ generics or lifetimes) /// * `fields`: The fields of this struct in the order they are defined /// - pub fn new(name: &'static str, fields: &[UnnamedField]) -> Self { + pub fn new(name: &'static str, fields: &[UnnamedField]) -> Self { Self { name, - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), } @@ -96,11 +99,11 @@ impl TupleStructInfo { self.name } - /// The [type name] of the tuple struct. + /// The [type path] of the tuple struct. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the tuple struct. @@ -199,19 +202,19 @@ impl GetTupleStructField for dyn TupleStruct { } /// A tuple struct which allows fields to be added at runtime. -#[derive(Default)] +#[derive(Default, TypePath)] pub struct DynamicTupleStruct { name: String, fields: Vec>, } impl DynamicTupleStruct { - /// Returns the type name of the tuple struct. + /// Returns the type path of the tuple struct. pub fn name(&self) -> &str { &self.name } - /// Sets the type name of the tuple struct. + /// Sets the type path of the tuple struct. pub fn set_name(&mut self, name: String) { self.name = name; } @@ -265,8 +268,8 @@ impl TupleStruct for DynamicTupleStruct { impl Reflect for DynamicTupleStruct { #[inline] - fn type_name(&self) -> &str { - self.name.as_str() + fn type_path(&self) -> &str { + &self.name } #[inline] @@ -411,7 +414,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.type_path()); for field in dyn_tuple_struct.iter_fields() { debug.field(&field as &dyn Debug); } diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 9e5519d500ad6..6b3ac70564a4e 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -1,5 +1,6 @@ use crate::{ - ArrayInfo, EnumInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, TupleStructInfo, + type_path, ArrayInfo, EnumInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, + TupleStructInfo, TypePath, }; use std::any::{Any, TypeId}; @@ -23,7 +24,7 @@ use std::any::{Any, TypeId}; /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectRef, StructInfo, TypeInfo, ValueInfo}; +/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectRef, StructInfo, TypeInfo, TypePath, ValueInfo}; /// # use bevy_reflect::utility::NonGenericTypeInfoCell; /// use bevy_reflect::Typed; /// @@ -46,9 +47,16 @@ use std::any::{Any, TypeId}; /// } /// } /// +/// # impl TypePath for MyStruct { +/// # fn type_path() -> &'static str { todo!() } +/// # fn short_type_name_base() -> &'static str { todo!() } +/// # fn short_type_name() -> &'static str { todo!() } +/// # fn module_path() -> &'static str { todo!() } +/// # fn crate_name() -> &'static str { todo!() } +/// # } /// # /// # impl Reflect for MyStruct { -/// # fn type_name(&self) -> &str { todo!() } +/// # fn type_path(&self) -> &str { todo!() } /// # fn get_type_info(&self) -> &'static TypeInfo { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } @@ -82,7 +90,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_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 @@ -92,7 +100,7 @@ pub trait Typed: Reflect { /// [`Reflect::get_type_info`]: crate::Reflect::get_type_info /// [`TypeRegistry::get_type_info`]: crate::TypeRegistry::get_type_info /// [`TypeId`]: std::any::TypeId -/// [type name]: std::any::type_name +/// [type path]: crate::TypePath #[derive(Debug, Clone)] pub enum TypeInfo { Struct(StructInfo), @@ -127,18 +135,18 @@ impl TypeInfo { /// The [name] of the underlying type. /// - /// [name]: std::any::type_name - pub fn type_name(&self) -> &'static str { + /// [name]: crate::TypePath + pub fn type_path(&self) -> &'static str { 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::Dynamic(info) => info.type_name(), + Self::Struct(info) => info.type_path(), + Self::TupleStruct(info) => info.type_path(), + Self::Tuple(info) => info.type_path(), + Self::List(info) => info.type_path(), + Self::Array(info) => info.type_path(), + Self::Map(info) => info.type_path(), + Self::Enum(info) => info.type_path(), + Self::Value(info) => info.type_path(), + Self::Dynamic(info) => info.type_path(), } } @@ -158,23 +166,23 @@ 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: &'static str, type_id: TypeId, } impl ValueInfo { - pub fn new() -> Self { + pub fn new() -> Self { Self { - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), } } - /// The [type name] of the value. + /// The [type path] of the value. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: crate::TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the value. @@ -198,23 +206,23 @@ impl ValueInfo { /// [`DynamicList`]: crate::DynamicList #[derive(Debug, Clone)] pub struct DynamicInfo { - type_name: &'static str, + type_path: &'static str, type_id: TypeId, } impl DynamicInfo { - pub fn new() -> Self { + pub fn new() -> Self { Self { - type_name: std::any::type_name::(), + type_path: type_path::(), type_id: TypeId::of::(), } } - /// The [type name] of the dynamic value. + /// The [type path] of the dynamic value. /// - /// [type name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_name + /// [type path]: crate::TypePath + pub fn type_path(&self) -> &'static str { + self.type_path } /// The [`TypeId`] of the dynamic value. diff --git a/crates/bevy_reflect/src/type_path.rs b/crates/bevy_reflect/src/type_path.rs new file mode 100644 index 0000000000000..efcd98b1c9213 --- /dev/null +++ b/crates/bevy_reflect/src/type_path.rs @@ -0,0 +1,192 @@ +use crate::utility::GenericTypePathCell; + +/// Provides the path of the type as a string. +/// +/// This is a stable alternative to [`std::any::type_name`] whose output isn't guarenteed +/// and may change between versions of the compiler. +/// +/// This trait may be derived via [`#[derive(TypePath)]`]. +/// +/// ## Manual implementation +/// +/// If you need to manually implement [`TypePath`], it's recommended to follow +/// the example below (unless specifying a custom name). +/// +/// ```ignore +/// bevy_reflect::TypePath; +/// +/// struct MyType; +/// +/// impl TypePath for MyType { +/// fn type_path() -> &'static str { +/// concat!(module_path!(), "::", "MyType") +/// } +/// +/// fn short_type_name_base() -> &'static str { +/// "MyType" +/// } +/// +/// fn short_type_name() -> &'static str { +/// "MyType" +/// } +/// +/// fn module_path() -> &'static str { +/// module_path!() +/// } +/// +/// fn crate_name() -> &'static str { +/// "my_crate" +/// } +/// } +/// ``` +/// +/// If your type is generic you must use +/// [`GenericTypePathCell`][crate::utility::GenericTypePathCell]. +/// +/// ```ignore +/// bevy_reflect::{TypePath, utility::GenericTypePathCell}; +/// +/// struct MyType(T); +/// +/// impl TypePath for MyType { +/// fn type_path() -> &'static str { +/// static CELL: GenericTypePathCell = GenericTypePathCell::new(); +/// CELL.get_or_insert::(|| { +/// format!(concat!(module_path!(), "::MyType<{}>"), T::type_path()) +/// }) +/// } +/// +/// fn short_type_name_base() -> &'static str { +/// const IDENT_POS: usize = module_path!().len() + 2; +/// const GENERIC_POS: usize = IDENT_POS + "MyType".len(); +/// &::type_path()[IDENT_POS..GENERIC_POS] +/// } +/// +/// fn short_type_name() -> &'static str { +/// const IDENT_POS: usize = module_path!().len() + 2; +/// &::type_path()[IDENT_POS..] +/// } +/// +/// fn module_path() -> &'static str { +/// &::type_path()[..module_path!().len()] +/// } +/// +/// fn crate_name() -> &'static str { +/// "my_crate" +/// } +/// } +/// ``` +pub trait TypePath: 'static { + /// Returns the full path of the type. + /// + /// This is a stable alternative to [`std::any::type_name`] whose output isn't guarenteed + /// and may change between versions of the compiler. + fn type_path() -> &'static str; + + /// The short type name, without generics. + /// + /// e.g. `MyType` + fn short_type_name_base() -> &'static str; + + /// The short type name, with generics. + /// + /// e.g. `MyType` + fn short_type_name() -> &'static str; + + /// The full type path, minus the actual type. + /// + /// e.g. `my_crate::my_mod` + fn module_path() -> &'static str; + + /// The crate name. + /// + /// e.g. `my_crate` + fn crate_name() -> &'static str; +} + +/// Returns the [type path] of `T`. +/// +/// [type path]: TypePath +pub fn type_path() -> &'static str { + T::type_path() +} + +macro_rules! impl_type_name_tuple { + ( + $($t:tt),* + ) => { + impl<$($t: TypePath),*> TypePath for ($($t,)*) { + #[allow(non_snake_case)] + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + $(let $t = <$t as TypePath>::type_path();)* + format!( + concat!("(", impl_type_name_tuple!(@bracket $($t),*), ")"), + $($t,)* + ) + }) + } + + fn short_type_name_base() -> &'static str { + // FIXME: how to handle tuple ? + Self::type_path() + } + + fn short_type_name() -> &'static str { + // FIXME: how to handle tuple ? + Self::type_path() + } + + fn module_path() -> &'static str { + // FIXME: how to handle tuple ? + "" + } + + fn crate_name() -> &'static str { + // FIXME: how to handle tuple ? + "" + } + } + }; + (@bracket $t:tt, $($rest:tt),*) => {concat!("{}, ", impl_type_name_tuple!(@bracket $($rest),*))}; + (@bracket $t:tt) => {"{}"}; + (@bracket) => {""}; +} + +impl_type_name_tuple!(); +impl_type_name_tuple!(A); +impl_type_name_tuple!(A, B); +impl_type_name_tuple!(A, B, C); +impl_type_name_tuple!(A, B, C, D); +impl_type_name_tuple!(A, B, C, D, E); +impl_type_name_tuple!(A, B, C, D, E, F); +impl_type_name_tuple!(A, B, C, D, E, F, G); +impl_type_name_tuple!(A, B, C, D, E, F, G, H); +impl_type_name_tuple!(A, B, C, D, E, F, G, H, I); +impl_type_name_tuple!(A, B, C, D, E, F, G, H, I, J); +impl_type_name_tuple!(A, B, C, D, E, F, G, H, I, J, K); +impl_type_name_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); + +#[cfg(test)] +mod tests { + use crate::{self as bevy_reflect, TypePath}; + + #[test] + fn tuple_name() { + #[derive(TypePath)] + #[type_path(path = "")] + struct Foo; + + #[derive(TypePath)] + #[type_path(path = "")] + struct Goo; + + #[derive(TypePath)] + #[type_path(path = "")] + struct Hoo; + + let s = <(Foo, Goo, Hoo) as TypePath>::type_path(); + assert_eq!(s, "(Foo, Goo, Hoo)"); + } +} diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index fe3302774b761..ead290cf3464e 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,4 +1,6 @@ -use crate::{serde::Serializable, Reflect, TypeInfo, Typed}; +use crate::{ + self as bevy_reflect, serde::Serializable, type_path, Reflect, TypeInfo, TypePath, Typed, +}; use bevy_ptr::{Ptr, PtrMut}; use bevy_utils::{HashMap, HashSet}; use downcast_rs::{impl_downcast, Downcast}; @@ -99,7 +101,7 @@ impl TypeRegistry { .insert(short_name, registration.type_id()); } self.full_name_to_id - .insert(registration.type_name().to_string(), registration.type_id()); + .insert(registration.type_path().to_string(), registration.type_id()); self.registrations .insert(registration.type_id(), registration); } @@ -120,12 +122,17 @@ impl TypeRegistry { /// type_registry.register_type_data::, ReflectSerialize>(); /// type_registry.register_type_data::, ReflectDeserialize>(); /// ``` - pub fn register_type_data>(&mut self) { + pub fn register_type_data< + T: Reflect + TypePath + 'static, + D: TypeData + TypePath + FromType, + >( + &mut self, + ) { let data = self.get_mut(TypeId::of::()).unwrap_or_else(|| { panic!( "attempted to call `TypeRegistry::register_type_data` for type `{T}` with data `{D}` without registering `{T}` first", - T = std::any::type_name::(), - D = std::any::type_name::(), + T = type_path::(), + D = type_path::(), ) }); data.insert(D::from_type()); @@ -155,9 +162,9 @@ impl TypeRegistry { /// given name. /// /// If no type with the given name has been registered, returns `None`. - pub fn get_with_name(&self, type_name: &str) -> Option<&TypeRegistration> { + pub fn get_with_name(&self, type_path: &str) -> Option<&TypeRegistration> { self.full_name_to_id - .get(type_name) + .get(type_path) .and_then(|id| self.get(*id)) } @@ -165,9 +172,9 @@ impl TypeRegistry { /// the given name. /// /// 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> { + pub fn get_with_name_mut(&mut self, type_path: &str) -> Option<&mut TypeRegistration> { self.full_name_to_id - .get(type_name) + .get(type_path) .cloned() .and_then(move |id| self.get_mut(id)) } @@ -323,11 +330,11 @@ impl TypeRegistration { } /// Creates type registration information for `T`. - pub fn of() -> Self { - let type_name = std::any::type_name::(); + pub fn of() -> Self { + let type_path = type_path::(); Self { data: HashMap::default(), - short_name: bevy_utils::get_short_name(type_name), + short_name: bevy_utils::get_short_name(type_path), type_info: T::type_info(), } } @@ -339,11 +346,11 @@ impl TypeRegistration { &self.short_name } - /// Returns the [name] of the type. + /// Returns the [type path] of the type. /// - /// [name]: std::any::type_name - pub fn type_name(&self) -> &'static str { - self.type_info.type_name() + /// [type path]: crate::TypePath + pub fn type_path(&self) -> &'static str { + self.type_info.type_path() } } @@ -390,17 +397,17 @@ pub trait FromType { /// /// A `ReflectSerialize` for type `T` can be obtained via /// [`FromType::from_type`]. -#[derive(Clone)] +#[derive(Clone, TypePath)] pub struct ReflectSerialize { get_serializable: for<'a> fn(value: &'a dyn Reflect) -> Serializable, } -impl FromType for ReflectSerialize { +impl FromType for ReflectSerialize { fn from_type() -> Self { ReflectSerialize { get_serializable: |value| { let value = value.downcast_ref::().unwrap_or_else(|| { - panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.type_name(), std::any::type_name::()) + panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.type_path(), type_path::()) }); Serializable::Borrowed(value) }, @@ -419,7 +426,7 @@ impl ReflectSerialize { /// /// A `ReflectDeserialize` for type `T` can be obtained via /// [`FromType::from_type`]. -#[derive(Clone)] +#[derive(Clone, TypePath)] pub struct ReflectDeserialize { pub func: fn( deserializer: &mut dyn erased_serde::Deserializer, diff --git a/crates/bevy_reflect/src/type_uuid.rs b/crates/bevy_reflect/src/type_uuid.rs index 5cfd46ab6a771..d0bd30d686453 100644 --- a/crates/bevy_reflect/src/type_uuid.rs +++ b/crates/bevy_reflect/src/type_uuid.rs @@ -1,6 +1,8 @@ pub use bevy_reflect_derive::TypeUuid; pub use bevy_utils::Uuid; +use crate::TypePath; + /// A trait for types with a statically associated UUID. pub trait TypeUuid { const TYPE_UUID: Uuid; @@ -9,23 +11,23 @@ pub trait TypeUuid { /// A trait for types with an associated UUID. pub trait TypeUuidDynamic { fn type_uuid(&self) -> Uuid; - fn type_name(&self) -> &'static str; + fn type_path(&self) -> &'static str; } impl TypeUuidDynamic for T where - T: TypeUuid, + T: TypeUuid + TypePath, { /// Returns the UUID associated with this value's type. fn type_uuid(&self) -> Uuid { Self::TYPE_UUID } - /// Returns the [type name] of this value's type. + /// Returns the [type path] of this value's type. /// - /// [type name]: std::any::type_name - fn type_name(&self) -> &'static str { - std::any::type_name::() + /// [type path]: crate::TypePath + fn type_path(&self) -> &'static str { + Self::type_path() } } diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index 1c331978f3f78..885d31f93670c 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -6,6 +6,67 @@ use once_cell::race::OnceBox; use parking_lot::RwLock; use std::any::{Any, TypeId}; +/// A container over non-generic types, allowing instances to be stored statically. +/// +/// This is specifically meant for use with _non_-generic types. If your type _is_ generic, +/// then use [`GenericDataCell`] instead. Otherwise, it will not take into account all +/// monomorphizations of your type. +pub struct NonGenericDataCell(OnceBox); + +impl NonGenericDataCell { + /// Initialize a [`NonGenericDataCell`] for non-generic types. + pub const fn new() -> Self { + Self(OnceBox::new()) + } + + /// Returns a reference to the `Data` stored in the cell. + /// + /// If there is no `Data` found, a new one will be generated from the given function. + pub fn get_or_set(&self, f: F) -> &Data + where + F: FnOnce() -> Data, + { + self.0.get_or_init(|| Box::new(f())) + } +} + +/// A container over generic types, allowing instances to be stored statically. +/// +/// This is specifically meant for use with generic types. If your type isn't generic, +/// then use [`NonGenericDataCell`] instead as it should be much more performant. +pub struct GenericDataCell(OnceBox>>); + +impl GenericDataCell { + /// Initialize a [`GenericDataCell`] for generic types. + pub const fn new() -> Self { + Self(OnceBox::new()) + } + + /// Returns a reference to the `Data` stored in the cell. + /// + /// This method will then return the correct `Data` reference for the given type `T`. + /// If there is no `Data` found, a new one will be generated from the given function. + pub fn get_or_insert(&self, f: F) -> &Data + where + T: Any + ?Sized, + F: FnOnce() -> Data, + { + let type_id = TypeId::of::(); + let mapping = self.0.get_or_init(|| Box::new(RwLock::default())); + if let Some(info) = mapping.read().get(&type_id) { + return info; + } + + // We leak here in order to obtain a `&'static` reference. + // Otherwise, we won't be able to return a reference due to the `RwLock`. + // This should be okay, though, since we expect it to remain statically + // available over the course of the application. + let value = Box::leak(Box::new(f())); + + mapping.write().entry(type_id).or_insert(value) + } +} + /// A container for [`TypeInfo`] over non-generic types, allowing instances to be stored statically. /// /// This is specifically meant for use with _non_-generic types. If your type _is_ generic, @@ -16,7 +77,7 @@ use std::any::{Any, TypeId}; /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectRef, StructInfo, Typed, TypeInfo}; +/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectRef, StructInfo, Typed, TypeInfo, TypePath}; /// use bevy_reflect::utility::NonGenericTypeInfoCell; /// /// struct Foo { @@ -33,9 +94,16 @@ use std::any::{Any, TypeId}; /// }) /// } /// } +/// # impl TypePath for Foo { +/// # fn type_path() -> &'static str { todo!() } +/// # fn short_type_name_base() -> &'static str { todo!() } +/// # fn short_type_name() -> &'static str { todo!() } +/// # fn module_path() -> &'static str { todo!() } +/// # fn crate_name() -> &'static str { todo!() } +/// # } /// # /// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } +/// # fn type_path(&self) -> &str { todo!() } /// # fn get_type_info(&self) -> &'static TypeInfo { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } @@ -49,26 +117,7 @@ use std::any::{Any, TypeId}; /// # fn clone_value(&self) -> Box { todo!() } /// # } /// ``` -pub struct NonGenericTypeInfoCell(OnceBox); - -impl NonGenericTypeInfoCell { - /// Initialize a [`NonGenericTypeInfoCell`] for non-generic types. - pub const fn new() -> Self { - Self(OnceBox::new()) - } - - /// Returns a reference to the [`TypeInfo`] stored in the cell. - /// - /// If there is no [`TypeInfo`] found, a new one will be generated from the given function. - /// - /// [`TypeInfos`]: TypeInfo - pub fn get_or_set(&self, f: F) -> &TypeInfo - where - F: FnOnce() -> TypeInfo, - { - self.0.get_or_init(|| Box::new(f())) - } -} +pub type NonGenericTypeInfoCell = NonGenericDataCell; /// A container for [`TypeInfo`] over generic types, allowing instances to be stored statically. /// @@ -79,12 +128,12 @@ impl NonGenericTypeInfoCell { /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{Reflect, ReflectMut, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField}; +/// # use bevy_reflect::{Reflect, ReflectMut, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField, TypePath}; /// use bevy_reflect::utility::GenericTypeInfoCell; /// /// struct Foo(T); /// -/// impl Typed for Foo { +/// impl Typed for Foo { /// fn type_info() -> &'static TypeInfo { /// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); /// CELL.get_or_insert::(|| { @@ -94,9 +143,17 @@ impl NonGenericTypeInfoCell { /// }) /// } /// } +/// +/// # impl TypePath for Foo { +/// # fn type_path() -> &'static str { todo!() } +/// # fn short_type_name_base() -> &'static str { todo!() } +/// # fn short_type_name() -> &'static str { todo!() } +/// # fn module_path() -> &'static str { todo!() } +/// # fn crate_name() -> &'static str { todo!() } +/// # } /// # /// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } +/// # fn type_path(&self) -> &str { todo!() } /// # fn get_type_info(&self) -> &'static TypeInfo { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } @@ -110,35 +167,59 @@ impl NonGenericTypeInfoCell { /// # fn clone_value(&self) -> Box { todo!() } /// # } /// ``` -pub struct GenericTypeInfoCell(OnceBox>>); +pub type GenericTypeInfoCell = GenericDataCell; -impl GenericTypeInfoCell { - /// Initialize a [`GenericTypeInfoCell`] for generic types. - pub const fn new() -> Self { - Self(OnceBox::new()) - } +/// A container for [`String`] over generic types, allowing instances to be stored statically. +/// +/// Used when implementing [`TypePath`][crate::TypePath] for generic types to store the type path. +/// +/// ## Example +/// +/// ``` +/// use bevy_reflect::{TypePath, utility::GenericTypePathCell}; +/// +/// struct MyType(T); +/// +/// impl TypePath for MyType { +/// fn type_path() -> &'static str { +/// static CELL: GenericTypePathCell = GenericTypePathCell::new(); +/// CELL.get_or_insert::(|| { +/// format!(concat!(module_path!(), "::MyType<{}>"), T::type_path()) +/// }) +/// } +/// +/// fn short_type_name_base() -> &'static str { +/// const IDENT_POS: usize = module_path!().len() + 2; +/// const GENERIC_POS: usize = IDENT_POS + "MyType".len(); +/// &::type_path()[IDENT_POS..GENERIC_POS] +/// } +/// +/// fn short_type_name() -> &'static str { +/// const IDENT_POS: usize = module_path!().len() + 2; +/// &::type_path()[IDENT_POS..] +/// } +/// +/// fn module_path() -> &'static str { +/// &::type_path()[..module_path!().len()] +/// } +/// +/// fn crate_name() -> &'static str { +/// "my_crate" +/// } +/// } +/// ``` +pub type GenericTypePathCell = GenericDataCell; - /// Returns a reference to the [`TypeInfo`] stored in the cell. - /// - /// This method will then return the correct [`TypeInfo`] reference for the given type `T`. - /// If there is no [`TypeInfo`] found, a new one will be generated from the given function. - pub fn get_or_insert(&self, f: F) -> &TypeInfo - where - T: Any + ?Sized, - F: FnOnce() -> TypeInfo, - { - let type_id = TypeId::of::(); - let mapping = self.0.get_or_init(|| Box::new(RwLock::default())); - if let Some(info) = mapping.read().get(&type_id) { - return info; +pub const fn crate_name_len(type_path: &str) -> usize { + const SEPARATOR: u8 = b':'; + let bytes = type_path.as_bytes(); + let end = bytes.len().saturating_sub(1); + let mut i = 0; + while i < end { + if bytes[i] == SEPARATOR && bytes[i + 1] == SEPARATOR { + return i; } - - mapping.write().entry(type_id).or_insert_with(|| { - // We leak here in order to obtain a `&'static` reference. - // Otherwise, we won't be able to return a reference due to the `RwLock`. - // This should be okay, though, since we expect it to remain statically - // available over the course of the application. - Box::leak(Box::new(f())) - }) + i += 1; } + bytes.len() } diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index 15d1a9fb0fa1a..7d64c34355fa8 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -12,7 +12,7 @@ use bevy_core::cast_slice; use bevy_derive::EnumVariantMeta; use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem}; use bevy_math::*; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::{tracing::error, Hashed}; use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator}; use thiserror::Error; @@ -25,7 +25,7 @@ pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; // TODO: allow values to be unloaded after been submitting to the GPU to conserve memory -#[derive(Debug, TypeUuid, Clone)] +#[derive(Debug, TypeUuid, Clone, TypePath)] #[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"] pub struct Mesh { primitive_topology: PrimitiveTopology, diff --git a/crates/bevy_render/src/mesh/mesh/skinning.rs b/crates/bevy_render/src/mesh/mesh/skinning.rs index 6d152689069ea..44cca8d773454 100644 --- a/crates/bevy_render/src/mesh/mesh/skinning.rs +++ b/crates/bevy_render/src/mesh/mesh/skinning.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ reflect::ReflectMapEntities, }; use bevy_math::Mat4; -use bevy_reflect::{Reflect, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeUuid}; use std::ops::Deref; #[derive(Component, Debug, Default, Clone, Reflect)] @@ -26,7 +26,7 @@ impl MapEntities for SkinnedMesh { } } -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "b9f155a9-54ec-4026-988f-e0a03e99a76f"] pub struct SkinnedMeshInverseBindposes(Box<[Mat4]>); diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index beee76e6f25a5..ab844d59d83ba 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -1,5 +1,5 @@ use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset}; -use bevy_reflect::{TypeUuid, Uuid}; +use bevy_reflect::{TypePath, TypeUuid, Uuid}; use bevy_utils::{tracing::error, BoxedFuture, HashMap}; use naga::back::wgsl::WriterFlags; use naga::valid::Capabilities; @@ -36,7 +36,7 @@ pub enum ShaderReflectError { } /// A shader, as defined by its [`ShaderSource`] and [`ShaderStage`](naga::ShaderStage) /// This is an "unprocessed" shader. It can contain preprocessor directives. -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "d95bc916-6c55-4de3-9622-37e7b6969fda"] pub struct Shader { source: Source, diff --git a/crates/bevy_render/src/texture/image.rs b/crates/bevy_render/src/texture/image.rs index 36a0708a992b8..160603e009bc7 100644 --- a/crates/bevy_render/src/texture/image.rs +++ b/crates/bevy_render/src/texture/image.rs @@ -15,7 +15,7 @@ use bevy_asset::HandleUntyped; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::system::{lifetimeless::SRes, Resource, SystemParamItem}; use bevy_math::Vec2; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use std::hash::Hash; use thiserror::Error; use wgpu::{ @@ -101,7 +101,7 @@ impl ImageFormat { } } -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"] pub struct Image { pub data: Vec, diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 16b7a3169cf7f..314cba19699ab 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ reflect::{ReflectComponent, ReflectMapEntities}, world::World, }; -use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeRegistryArc, TypeUuid}; use serde::Serialize; /// A collection of serializable dynamic entities, each with its own run-time defined set of components. @@ -16,7 +16,7 @@ use serde::Serialize; /// * adding the [`Handle`](bevy_asset::Handle) to an entity (the scene will only be /// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and /// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components) -#[derive(Default, TypeUuid)] +#[derive(Default, TypeUuid, TypePath)] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] pub struct DynamicScene { pub entities: Vec, @@ -75,14 +75,14 @@ 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()) + .get_with_name(component.type_path()) .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component.type_name().to_string(), + type_name: component.type_path().to_string(), })?; let reflect_component = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredComponent { - type_name: component.type_name().to_string(), + type_name: component.type_path().to_string(), } })?; diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 71855a9f56635..3b7e8a2f81183 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -4,7 +4,7 @@ use bevy_ecs::{ reflect::{ReflectComponent, ReflectMapEntities}, world::World, }; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use crate::{InstanceInfo, SceneSpawnError}; @@ -14,7 +14,7 @@ use crate::{InstanceInfo, SceneSpawnError}; /// * adding the [`Handle`](bevy_asset::Handle) to an entity (the scene will only be /// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and /// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components) -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "c156503c-edd9-4ec7-8d33-dab392df03cd"] pub struct Scene { pub world: World, diff --git a/crates/bevy_sprite/src/mesh2d/color_material.rs b/crates/bevy_sprite/src/mesh2d/color_material.rs index b0f68db9795e4..3f89ab3675c8d 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.rs +++ b/crates/bevy_sprite/src/mesh2d/color_material.rs @@ -1,7 +1,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; use bevy_math::Vec4; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ color::Color, prelude::Shader, render_asset::RenderAssets, render_resource::*, texture::Image, }; @@ -38,7 +38,7 @@ impl Plugin for ColorMaterialPlugin { } /// A [2d material](Material2d) that renders [2d meshes](crate::Mesh2dHandle) with a texture tinted by a uniform color -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "e228a544-e3ca-4e1e-bb9d-4d8bc1ad8c19"] #[uniform(0, ColorMaterialUniform)] pub struct ColorMaterial { diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 5dd2bc5b206b3..1f0265c0f9c36 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -14,7 +14,7 @@ use bevy_ecs::{ world::FromWorld, }; use bevy_log::error; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ extract_component::ExtractComponentPlugin, mesh::{Mesh, MeshVertexBufferLayout}, @@ -60,11 +60,11 @@ use crate::{ /// ``` /// # use bevy_sprite::{Material2d, MaterialMesh2dBundle}; /// # use bevy_ecs::prelude::*; -/// # use bevy_reflect::TypeUuid; +/// # use bevy_reflect::{TypeUuid, TypePath}; /// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image, color::Color}; /// # use bevy_asset::{Handle, AssetServer, Assets}; /// -/// #[derive(AsBindGroup, TypeUuid, Debug, Clone)] +/// #[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] /// #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] /// pub struct CustomMaterial { /// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to @@ -111,7 +111,9 @@ use crate::{ /// @group(1) @binding(2) /// var color_sampler: sampler; /// ``` -pub trait Material2d: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'static { +pub trait Material2d: + AsBindGroup + Send + Sync + Clone + TypeUuid + TypePath + Sized + 'static +{ /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader /// will be used. fn vertex_shader() -> ShaderRef { diff --git a/crates/bevy_sprite/src/texture_atlas.rs b/crates/bevy_sprite/src/texture_atlas.rs index 53cc9cadd6be2..9fa693047cdc7 100644 --- a/crates/bevy_sprite/src/texture_atlas.rs +++ b/crates/bevy_sprite/src/texture_atlas.rs @@ -2,14 +2,14 @@ use crate::Anchor; use bevy_asset::Handle; use bevy_ecs::component::Component; use bevy_math::{Rect, Vec2}; -use bevy_reflect::{Reflect, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeUuid}; use bevy_render::{color::Color, texture::Image}; use bevy_utils::HashMap; /// An atlas containing multiple textures (like a spritesheet or a tilemap). /// [Example usage animating sprite.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs) /// [Example usage loading sprite sheet.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs) -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"] pub struct TextureAtlas { /// The handle to the texture in which the sprites are stored diff --git a/crates/bevy_text/src/font.rs b/crates/bevy_text/src/font.rs index 56fe1e60fc0de..72d65b6e2a4e2 100644 --- a/crates/bevy_text/src/font.rs +++ b/crates/bevy_text/src/font.rs @@ -1,11 +1,11 @@ use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph}; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ render_resource::{Extent3d, TextureDimension, TextureFormat}, texture::Image, }; -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "97059ac6-c9ba-4da9-95b6-bed82c3ce198"] pub struct Font { pub font: FontArc, diff --git a/crates/bevy_text/src/font_atlas_set.rs b/crates/bevy_text/src/font_atlas_set.rs index 2649e37243391..696f3cd8b5b0e 100644 --- a/crates/bevy_text/src/font_atlas_set.rs +++ b/crates/bevy_text/src/font_atlas_set.rs @@ -2,6 +2,7 @@ use crate::{error::TextError, Font, FontAtlas, TextSettings}; use ab_glyph::{GlyphId, OutlinedGlyph, Point}; use bevy_asset::{Assets, Handle}; use bevy_math::Vec2; +use bevy_reflect::TypePath; use bevy_reflect::TypeUuid; use bevy_render::texture::Image; use bevy_sprite::TextureAtlas; @@ -10,7 +11,7 @@ use bevy_utils::HashMap; type FontSizeKey = FloatOrd; -#[derive(TypeUuid)] +#[derive(TypeUuid, TypePath)] #[uuid = "73ba778b-b6b5-4f45-982d-d21b6b86ace2"] pub struct FontAtlasSet { font_atlases: HashMap>, diff --git a/crates/bevy_utils/src/short_names.rs b/crates/bevy_utils/src/short_names.rs index 1c30bf61f9d0f..950edc8033b08 100644 --- a/crates/bevy_utils/src/short_names.rs +++ b/crates/bevy_utils/src/short_names.rs @@ -1,4 +1,4 @@ -/// Shortens a type name to remove all module paths. +/// Shortens a type path to remove all module paths. /// /// The short name of a type is its full name as returned by /// [`std::any::type_name`], but with the prefix of all paths removed. For diff --git a/examples/3d/lines.rs b/examples/3d/lines.rs index a6d61c401b819..c4944a18d7916 100644 --- a/examples/3d/lines.rs +++ b/examples/3d/lines.rs @@ -62,7 +62,7 @@ fn setup( }); } -#[derive(Default, AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(Default, AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "050ce6ac-080a-4d8c-b6b5-b5bab7560d8f"] struct LineMaterial { #[uniform(0)] diff --git a/examples/3d/skybox.rs b/examples/3d/skybox.rs index 03dbfa0aacbc5..484c396a626f7 100644 --- a/examples/3d/skybox.rs +++ b/examples/3d/skybox.rs @@ -196,7 +196,7 @@ fn animate_light_direction( } } -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "9509a0f8-3c05-48ee-a13e-a93226c7f488"] struct CubemapMaterial { base_color_texture: Option>, diff --git a/examples/asset/custom_asset.rs b/examples/asset/custom_asset.rs index 50089d5dfb5fb..4f97f1a24547a 100644 --- a/examples/asset/custom_asset.rs +++ b/examples/asset/custom_asset.rs @@ -8,7 +8,7 @@ use bevy::{ }; use serde::Deserialize; -#[derive(Debug, Deserialize, TypeUuid)] +#[derive(Debug, Deserialize, TypeUuid, TypePath)] #[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"] pub struct CustomAsset { pub value: i32, diff --git a/examples/reflection/generic_reflection.rs b/examples/reflection/generic_reflection.rs index b91a4fa0733bc..47f3473b83ca8 100644 --- a/examples/reflection/generic_reflection.rs +++ b/examples/reflection/generic_reflection.rs @@ -12,8 +12,10 @@ fn main() { .run(); } +/// Because `#[derive(Reflect)]` also derives `TypePath` for `MyType`, +/// the generic parameter must also implement `TypePath`. #[derive(Reflect)] -struct MyType { +struct MyType { value: T, } diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index cb5d09038a4a3..e10f8b9cf8170 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -31,7 +31,7 @@ fn setup( }); } -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "a3d71c04-d054-4946-80f8-ba6cfbc90cad"] struct CustomMaterial {} diff --git a/examples/shader/array_texture.rs b/examples/shader/array_texture.rs index 4f935369a00d2..8c06ab2c94abe 100644 --- a/examples/shader/array_texture.rs +++ b/examples/shader/array_texture.rs @@ -89,7 +89,7 @@ fn create_array_texture( } } -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "9c5a0ddf-1eaf-41b4-9832-ed736fd26af3"] struct ArrayTextureMaterial { #[texture(0, dimension = "2d_array")] diff --git a/examples/shader/custom_vertex_attribute.rs b/examples/shader/custom_vertex_attribute.rs index e98ca56a24b7e..a0a23f85c6ce6 100644 --- a/examples/shader/custom_vertex_attribute.rs +++ b/examples/shader/custom_vertex_attribute.rs @@ -57,7 +57,7 @@ fn setup( } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index d52cbc00a6e9b..274904ec62871 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -164,7 +164,7 @@ fn main_camera_cube_rotator_system( // Region below declares of the custom material handling post processing effect /// Our custom post processing material -#[derive(AsBindGroup, TypeUuid, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Clone)] #[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"] struct PostProcessingMaterial { /// In this example, this image will be the result of the main camera. diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index ca9d953d8ff2f..0f757f87060c1 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -75,7 +75,7 @@ impl Material for CustomMaterial { } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] #[bind_group_data(CustomMaterialKey)] pub struct CustomMaterial { diff --git a/examples/shader/shader_material.rs b/examples/shader/shader_material.rs index d501d6c092952..37b94883b42b3 100644 --- a/examples/shader/shader_material.rs +++ b/examples/shader/shader_material.rs @@ -53,7 +53,7 @@ impl Material for CustomMaterial { } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] diff --git a/examples/shader/shader_material_glsl.rs b/examples/shader/shader_material_glsl.rs index fe7e47c86f6ab..cffd9519ddfd7 100644 --- a/examples/shader/shader_material_glsl.rs +++ b/examples/shader/shader_material_glsl.rs @@ -47,7 +47,7 @@ fn setup( } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, Clone, TypeUuid)] +#[derive(AsBindGroup, Clone, TypeUuid, TypePath)] #[uuid = "4ee9c363-1124-4113-890e-199d81b00281"] pub struct CustomMaterial { #[uniform(0)] diff --git a/examples/shader/shader_material_screenspace_texture.rs b/examples/shader/shader_material_screenspace_texture.rs index 9fd63f8588d5a..cb8dc9c770ec7 100644 --- a/examples/shader/shader_material_screenspace_texture.rs +++ b/examples/shader/shader_material_screenspace_texture.rs @@ -66,7 +66,7 @@ fn rotate_camera(mut camera: Query<&mut Transform, With>, time: Res< cam_transform.look_at(Vec3::ZERO, Vec3::Y); } -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "b62bb455-a72c-4b56-87bb-81e0554e234f"] pub struct CustomMaterial { #[texture(0)]