reflect: stable type path v2 (#7184)

# Objective

- Introduce a stable alternative to
[`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html).
- Rewrite of #5805 with heavy inspiration in design.
- On the path to #5830.
- Part of solving #3327.


## Solution

- Add a `TypePath` trait for static stable type path/name information.
- Add a `TypePath` derive macro.
- Add a `impl_type_path` macro for implementing internal and foreign
types in `bevy_reflect`.

---

## Changelog

- Added `TypePath` trait.
- Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`.
- Added a `TypePath` derive macro.
- Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on
internal and foreign types in `bevy_reflect`.
- Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to
`(Non)GenericTypedCell<T>` which allows us to be generic over both
`TypeInfo` and `TypePath`.
- `TypePath` is now a supertrait of `Asset`, `Material` and
`Material2d`.
- `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be
specified.
- `impl_reflect_value` needs to either specify path starting with a
double colon (`::core::option::Option`) or an `in my_crate::foo`
declaration.
- Added `bevy_reflect_derive::ReflectTypePath`.
- Most uses of `Ident` in `bevy_reflect_derive` changed to use
`ReflectTypePath`.

## Migration Guide

- Implementors of `Asset`, `Material` and `Material2d` now also need to
derive `TypePath`.
- Manual implementors of `Reflect` will need to implement the new
`get_type_path` method.

## Open Questions
- [x] ~This PR currently does not migrate any usages of
`std::any::type_name` to use `bevy_reflect::TypePath` to ease the review
process. Should it?~ Migration will be left to a follow-up PR.
- [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to
satisfy new bounds, mostly when deriving `TypeUuid`. Should we make
`TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in
favour of
`TypePath`?](2afbd85532 (r961067892))
This commit is contained in:
radiish 2023-06-05 21:31:20 +01:00 committed by GitHub
parent 94dce091a9
commit 1efc762924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 2064 additions and 413 deletions

View File

@ -98,7 +98,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;
///
@ -647,10 +647,10 @@ mod test {
use crate::{loader::LoadedAsset, update_asset_storage_system};
use bevy_app::{App, Update};
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;

View File

@ -488,7 +488,7 @@ mod tests {
#[test]
fn asset_overwriting() {
#[derive(bevy_reflect::TypeUuid)]
#[derive(bevy_reflect::TypeUuid, bevy_reflect::TypePath)]
#[uuid = "44115972-f31b-46e5-be5c-2b9aece6a52f"]
struct MyAsset;
let mut app = App::new();

View File

@ -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 + TypePath + AssetDynamic {}
/// An untyped version of the [`Asset`] trait.
pub trait AssetDynamic: Downcast + TypeUuidDynamic + Send + Sync + 'static {}
impl_downcast!(AssetDynamic);
impl<T> Asset for T where T: TypeUuid + AssetDynamic + TypeUuidDynamic {}
impl<T> Asset for T where T: TypeUuid + TypePath + AssetDynamic + TypeUuidDynamic {}
impl<T> AssetDynamic for T where T: Send + Sync + 'static + TypeUuidDynamic {}

View File

@ -1,11 +1,11 @@
use anyhow::Result;
use bevy_asset::{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.

View File

@ -1,5 +1,5 @@
use bevy_math::Vec3;
use bevy_reflect::TypeUuid;
use bevy_reflect::{TypePath, TypeUuid};
use bevy_transform::prelude::Transform;
use rodio::{Sink, SpatialSink};
@ -87,7 +87,7 @@ pub trait AudioSinkPlayback {
/// }
/// ```
///
#[derive(TypeUuid)]
#[derive(TypePath, TypeUuid)]
#[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.
@ -158,7 +158,7 @@ impl AudioSinkPlayback for AudioSink {
/// }
/// ```
///
#[derive(TypeUuid)]
#[derive(TypePath, TypeUuid)]
#[uuid = "F3CA4C47-595E-453B-96A7-31C3DDF2A177"]
pub struct SpatialAudioSink {
// This field is an Option in order to allow us to have a safe drop that will detach the sink.

View File

@ -407,7 +407,7 @@ impl<C: Resource + Reflect + FromWorld> FromType<C> for ReflectResource {
}
}
impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!((in bevy_ecs) Entity(Hash, PartialEq, Serialize, Deserialize));
impl_from_reflect_value!(Entity);
#[derive(Clone)]

View File

@ -12,7 +12,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, MeshVertexAttribute},
renderer::RenderDevice,
@ -58,7 +58,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<Handle<Scene>>,
@ -78,7 +78,7 @@ pub struct Gltf {
/// A glTF node with all of its child nodes, its [`GltfMesh`],
/// [`Transform`](bevy_transform::prelude::Transform) and an optional [`GltfExtras`].
#[derive(Debug, Clone, TypeUuid)]
#[derive(Debug, Clone, TypeUuid, TypePath)]
#[uuid = "dad74750-1fd6-460f-ac51-0a7937563865"]
pub struct GltfNode {
pub children: Vec<GltfNode>,
@ -89,7 +89,7 @@ pub struct GltfNode {
/// A glTF mesh, which may consist of multiple [`GltfPrimitives`](GltfPrimitive)
/// and an optional [`GltfExtras`].
#[derive(Debug, Clone, TypeUuid)]
#[derive(Debug, Clone, TypeUuid, TypePath)]
#[uuid = "8ceaec9a-926a-4f29-8ee3-578a69f42315"]
pub struct GltfMesh {
pub primitives: Vec<GltfPrimitive>,
@ -97,7 +97,7 @@ pub struct GltfMesh {
}
/// Part of a [`GltfMesh`] that consists of a [`Mesh`], an optional [`StandardMaterial`] and [`GltfExtras`].
#[derive(Debug, Clone, TypeUuid)]
#[derive(Debug, Clone, TypeUuid, TypePath)]
#[uuid = "cbfca302-82fd-41cb-af77-cab6b3d50af1"]
pub struct GltfPrimitive {
pub mesh: Handle<Mesh>,

View File

@ -174,10 +174,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(TypePath, Copy, Clone, Eq, PartialEq, Hash)]
enum DummyInput {
Input1,
Input2,

View File

@ -19,7 +19,7 @@ use bevy_ecs::{
SystemParamItem,
},
};
use bevy_reflect::TypeUuid;
use bevy_reflect::{TypePath, TypeUuid};
use bevy_render::{
extract_component::ExtractComponentPlugin,
mesh::{Mesh, MeshVertexBufferLayout},
@ -59,11 +59,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
@ -106,7 +106,7 @@ 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 {
/// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader
/// will be used.
fn vertex_shader() -> ShaderRef {

View File

@ -1,15 +1,22 @@
use crate::container_attributes::ReflectTraits;
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
use crate::fq_std::{FQAny, FQDefault, FQSend, FQSync};
use crate::utility::{members_to_serialization_denylist, WhereClauseOptions};
use crate::type_path::parse_path_no_leading_colon;
use crate::utility::{members_to_serialization_denylist, StringExpr, WhereClauseOptions};
use bit_set::BitSet;
use quote::quote;
use quote::{quote, ToTokens};
use syn::token::Comma;
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
use crate::{
utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
TYPE_PATH_ATTRIBUTE_NAME,
};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Variant};
use syn::{
parse_str, Data, DeriveInput, Field, Fields, GenericParam, Generics, Ident, LitStr, Meta, Path,
PathSegment, Type, TypeParam, Variant,
};
pub(crate) enum ReflectDerive<'a> {
Struct(ReflectStruct<'a>),
@ -36,9 +43,7 @@ pub(crate) struct ReflectMeta<'a> {
/// The registered traits for this type.
traits: ReflectTraits,
/// The name of this type.
type_name: &'a Ident,
/// The generics defined on this type.
generics: &'a Generics,
type_path: ReflectTypePath<'a>,
/// A cached instance of the path to the `bevy_reflect` crate.
bevy_reflect_path: Path,
/// The documentation for this type, if any
@ -131,8 +136,12 @@ enum ReflectMode {
impl<'a> ReflectDerive<'a> {
pub fn from_input(input: &'a DeriveInput) -> Result<Self, syn::Error> {
let mut traits = ReflectTraits::default();
// Should indicate whether `#[reflect_value]` was used
// Should indicate whether `#[reflect_value]` was used.
let mut reflect_mode = None;
// Should indicate whether `#[type_path = "..."]` was used.
let mut custom_path: Option<Path> = None;
// Should indicate whether `#[type_name = "..."]` was used.
let mut custom_type_name: Option<Ident> = None;
#[cfg(feature = "documentation")]
let mut doc = crate::documentation::Documentation::default();
@ -177,6 +186,35 @@ impl<'a> ReflectDerive<'a> {
reflect_mode = Some(ReflectMode::Value);
}
Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => {
let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}) = &pair.value else {
return Err(syn::Error::new(
pair.span(),
format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
));
};
custom_path = Some(syn::parse::Parser::parse_str(
parse_path_no_leading_colon,
&lit.value(),
)?);
}
Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => {
let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}) = &pair.value else {
return Err(syn::Error::new(
pair.span(),
format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
));
};
custom_type_name = Some(parse_str(&lit.value())?);
}
#[cfg(feature = "documentation")]
Meta::NameValue(pair) if pair.path.is_ident("doc") => {
if let syn::Expr::Lit(syn::ExprLit {
@ -190,8 +228,27 @@ impl<'a> ReflectDerive<'a> {
_ => continue,
}
}
match (&mut custom_path, custom_type_name) {
(Some(path), custom_type_name) => {
let ident = custom_type_name.unwrap_or_else(|| input.ident.clone());
path.segments.push(PathSegment::from(ident));
}
(None, Some(name)) => {
return Err(syn::Error::new(
name.span(),
format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."),
));
}
_ => (),
}
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
let type_path = ReflectTypePath::Internal {
ident: &input.ident,
custom_path,
generics: &input.generics,
};
let meta = ReflectMeta::new(type_path, traits);
#[cfg(feature = "documentation")]
let meta = meta.with_docs(doc);
@ -233,6 +290,16 @@ impl<'a> ReflectDerive<'a> {
};
}
pub fn meta(&self) -> &ReflectMeta<'a> {
match self {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => data.meta(),
ReflectDerive::Enum(data) => data.meta(),
ReflectDerive::Value(meta) => meta,
}
}
fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
let sifter: utility::ResultSifter<StructField<'a>> = fields
.iter()
@ -288,11 +355,10 @@ 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_path: ReflectTypePath<'a>, traits: ReflectTraits) -> Self {
Self {
traits,
type_name,
generics,
type_path,
bevy_reflect_path: utility::get_bevy_reflect_path(),
#[cfg(feature = "documentation")]
docs: Default::default(),
@ -311,13 +377,8 @@ impl<'a> ReflectMeta<'a> {
}
/// The name of this struct.
pub fn type_name(&self) -> &'a Ident {
self.type_name
}
/// The generics associated with this struct.
pub fn generics(&self) -> &'a Generics {
self.generics
pub fn type_path(&self) -> &ReflectTypePath<'a> {
&self.type_path
}
/// The cached `bevy_reflect` path.
@ -330,14 +391,7 @@ impl<'a> ReflectMeta<'a> {
&self,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
crate::registration::impl_get_type_registration(
self.type_name,
&self.bevy_reflect_path,
self.traits.idents(),
self.generics,
where_clause_options,
None,
)
crate::registration::impl_get_type_registration(self, where_clause_options, None)
}
/// The collection of docstrings for this type, if any.
@ -368,13 +422,8 @@ impl<'a> ReflectStruct<'a> {
&self,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let reflect_path = self.meta.bevy_reflect_path();
crate::registration::impl_get_type_registration(
self.meta.type_name(),
reflect_path,
self.meta.traits().idents(),
self.meta.generics(),
self.meta(),
where_clause_options,
Some(&self.serialization_denylist),
)
@ -421,6 +470,7 @@ impl<'a> ReflectStruct<'a> {
active_trait_bounds: quote! { #bevy_reflect_path::Reflect },
ignored_types: self.ignored_types().into(),
ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync },
..WhereClauseOptions::type_path_bounds(self.meta())
}
}
}
@ -433,7 +483,7 @@ impl<'a> ReflectEnum<'a> {
/// Returns the given ident as a qualified unit variant of this enum.
pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
let name = self.meta.type_name;
let name = self.meta.type_path();
quote! {
#name::#variant
}
@ -479,6 +529,7 @@ impl<'a> ReflectEnum<'a> {
active_trait_bounds: quote! { #bevy_reflect_path::FromReflect },
ignored_types: self.ignored_types().into(),
ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync + #FQDefault },
..WhereClauseOptions::type_path_bounds(self.meta())
}
}
}
@ -509,3 +560,355 @@ impl<'a> EnumVariant<'a> {
}
}
}
/// Represents a path to a type.
///
/// This is used over [`struct@Ident`] or [`Path`]
/// to have the correct semantics for [deriving `TypePath`].
///
/// The type can always be reached with its [`ToTokens`] implementation.
///
/// The [`short_type_path`], [`type_ident`], [`crate_name`], and [`module_path`] methods
/// have corresponding methods on the `TypePath` trait.
/// [`long_type_path`] corresponds to the `type_path` method on `TypePath`.
///
/// [deriving `TypePath`]: crate::derive_type_path
/// [`long_type_path`]: ReflectTypePath::long_type_path
/// [`short_type_path`]: ReflectTypePath::short_type_path
/// [`type_ident`]: ReflectTypePath::type_ident
/// [`crate_name`]: ReflectTypePath::crate_name
/// [`module_path`]: ReflectTypePath::module_path
///
/// # Example
///
/// ```rust,ignore
/// # use syn::parse_quote;
/// # use bevy_reflect_derive::ReflectTypePath;
/// let path: syn::Path = parse_quote!(::core::marker::PhantomData)?;
///
/// let type_path = ReflectTypePath::External {
/// path,
/// custom_path: None,
/// };
///
/// // Eqivalent to "core::marker".
/// let module_path = type_path.module_path();
/// # Ok::<(), syn::Error>(())
/// ```
///
pub(crate) enum ReflectTypePath<'a> {
/// Types without a crate/module that can be named from any scope (e.g. `bool`).
Primitive(&'a Ident),
/// Using `::my_crate::foo::Bar` syntax.
///
/// May have a seperate custom path used for the `TypePath` implementation.
External {
path: &'a Path,
custom_path: Option<Path>,
generics: &'a Generics,
},
/// The name of a type relative to its scope.
///
/// The type must be able to be reached with just its name.
///
/// May have a seperate alias path used for the `TypePath` implementation.
///
/// Module and crate are found with [`module_path!()`](core::module_path),
/// if there is no custom path specified.
Internal {
ident: &'a Ident,
custom_path: Option<Path>,
generics: &'a Generics,
},
/// Any [`syn::Type`] with only a defined `type_path` and `short_type_path`.
#[allow(dead_code)]
// Not currently used but may be useful in the future due to its generality.
Anonymous {
qualified_type: Type,
long_type_path: StringExpr,
short_type_path: StringExpr,
},
}
impl<'a> ReflectTypePath<'a> {
/// Returns the path interpreted as an [`struct@Ident`].
///
/// Returns [`None`] if [anonymous].
///
/// [anonymous]: ReflectTypePath::Anonymous
pub fn get_ident(&self) -> Option<&Ident> {
match self {
Self::Internal {
ident, custom_path, ..
} => Some(
custom_path
.as_ref()
.map(|path| &path.segments.last().unwrap().ident)
.unwrap_or(ident),
),
Self::External {
path, custom_path, ..
} => Some(
&custom_path
.as_ref()
.unwrap_or(path)
.segments
.last()
.unwrap()
.ident,
),
Self::Primitive(ident) => Some(ident),
_ => None,
}
}
/// The generics associated with the type.
///
/// Empty if [anonymous] or [primitive].
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
pub fn generics(&self) -> &'a Generics {
// Use a constant because we need to return a reference of at least 'a.
const EMPTY_GENERICS: &Generics = &Generics {
gt_token: None,
lt_token: None,
where_clause: None,
params: Punctuated::new(),
};
match self {
Self::Internal { generics, .. } | Self::External { generics, .. } => generics,
_ => EMPTY_GENERICS,
}
}
/// Whether an implementation of `Typed` or `TypePath` should be generic.
///
/// Returning true that it should use a `GenericTypeCell` in its implementation.
pub fn impl_is_generic(&self) -> bool {
// Whether to use `GenericTypeCell` is not dependent on lifetimes
// (which all have to be 'static anyway).
!self
.generics()
.params
.iter()
.all(|param| matches!(param, GenericParam::Lifetime(_)))
}
/// Returns the path interpreted as a [`Path`].
///
/// Returns [`None`] if [anonymous], [primitive],
/// or a [`ReflectTypePath::Internal`] without a custom path.
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
pub fn get_path(&self) -> Option<&Path> {
match self {
Self::Internal { custom_path, .. } => custom_path.as_ref(),
Self::External {
path, custom_path, ..
} => Some(custom_path.as_ref().unwrap_or(path)),
_ => None,
}
}
/// Returns whether this [internal] or [external] path has a custom path.
///
/// [internal]: ReflectTypePath::Internal
/// [external]: ReflectTypePath::External
pub fn has_custom_path(&self) -> bool {
match self {
Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => {
custom_path.is_some()
}
_ => false,
}
}
/// Returns a [`StringExpr`] representing the name of the type's crate.
///
/// Returns [`None`] if the type is [primitive] or [anonymous].
///
/// For non-customised [internal] paths this is created from [`module_path`].
///
/// For `Option<PhantomData>`, this is `"core"`.
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
/// [internal]: ReflectTypePath::Internal
pub fn crate_name(&self) -> Option<StringExpr> {
if let Some(path) = self.get_path() {
let crate_name = &path.segments.first().unwrap().ident;
return Some(StringExpr::from(crate_name));
}
match self {
Self::Internal { .. } => Some(StringExpr::Borrowed(quote! {
::core::module_path!()
.split(':')
.next()
.unwrap()
})),
Self::External { .. } => unreachable!(),
_ => None,
}
}
/// Combines type generics and const generics into one [`StringExpr`].
///
/// This string can be used with a `GenericTypePathCell` in a `TypePath` implementation.
///
/// The `ty_generic_fn` param maps [`TypeParam`]s to [`StringExpr`]s.
fn reduce_generics(
generics: &Generics,
mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,
) -> StringExpr {
let mut params = generics.params.iter().filter_map(|param| match param {
GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),
GenericParam::Const(const_param) => {
let ident = &const_param.ident;
let ty = &const_param.ty;
Some(StringExpr::Owned(quote! {
<#ty as ::std::string::ToString>::to_string(&#ident)
}))
}
GenericParam::Lifetime(_) => None,
});
params
.next()
.into_iter()
.chain(params.flat_map(|x| [StringExpr::from_str(", "), x]))
.collect()
}
/// Returns a [`StringExpr`] representing the "type path" of the type.
///
/// For `Option<PhantomData>`, this is `"core::option::Option<core::marker::PhantomData>"`.
pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
match self {
Self::Primitive(ident) => StringExpr::from(ident),
Self::Anonymous { long_type_path, .. } => long_type_path.clone(),
Self::Internal { generics, .. } | Self::External { generics, .. } => {
let ident = self.type_ident().unwrap();
let module_path = self.module_path().unwrap();
if self.impl_is_generic() {
let generics = ReflectTypePath::reduce_generics(
generics,
|TypeParam { ident, .. }| {
StringExpr::Borrowed(quote! {
<#ident as #bevy_reflect_path::TypePath>::type_path()
})
},
);
StringExpr::from_iter([
module_path,
StringExpr::from_str("::"),
ident,
StringExpr::from_str("<"),
generics,
StringExpr::from_str(">"),
])
} else {
StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident])
}
}
}
}
/// Returns a [`StringExpr`] representing the "short path" of the type.
///
/// For `Option<PhantomData>`, this is `"Option<PhantomData>"`.
pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
match self {
Self::Anonymous {
short_type_path, ..
} => short_type_path.clone(),
Self::Primitive(ident) => StringExpr::from(ident),
Self::External { generics, .. } | Self::Internal { generics, .. } => {
let ident = self.type_ident().unwrap();
if self.impl_is_generic() {
let generics = ReflectTypePath::reduce_generics(
generics,
|TypeParam { ident, .. }| {
StringExpr::Borrowed(quote! {
<#ident as #bevy_reflect_path::TypePath>::short_type_path()
})
},
);
StringExpr::from_iter([
ident,
StringExpr::from_str("<"),
generics,
StringExpr::from_str(">"),
])
} else {
ident
}
}
}
}
/// Returns a [`StringExpr`] representing the path to the module
/// that the type is in.
///
/// Returns [`None`] if the type is [primitive] or [anonymous].
///
/// For non-customised [internal] paths this is created from [`module_path`].
///
/// For `Option<PhantomData>`, this is `"core::option"`.
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
/// [internal]: ReflectTypePath::Internal
pub fn module_path(&self) -> Option<StringExpr> {
if let Some(path) = self.get_path() {
let path_string = path
.segments
.pairs()
.take(path.segments.len() - 1)
.map(|pair| pair.value().ident.to_string())
.reduce(|path, ident| path + "::" + &ident)
.unwrap();
let path_lit = LitStr::new(&path_string, path.span());
return Some(StringExpr::from_lit(&path_lit));
}
match self {
Self::Internal { .. } => Some(StringExpr::Const(quote! {
::core::module_path!()
})),
Self::External { .. } => unreachable!(),
_ => None,
}
}
/// Returns a [`StringExpr`] representing the type's final ident.
///
/// Returns [`None`] if the type is [anonymous].
///
/// This is not necessarily a valid qualified path to the type.
///
/// For `Option<PhantomData>`, this is `"Option"`.
///
/// [anonymous]: ReflectTypePath::Anonymous
pub fn type_ident(&self) -> Option<StringExpr> {
self.get_ident().map(StringExpr::from)
}
}
impl<'a> ToTokens for ReflectTypePath<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens),
Self::External { path, .. } => path.to_tokens(tokens),
Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens),
}
}
}

View File

@ -22,13 +22,13 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
/// Implements `FromReflect` for the given value type
pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
let type_name = meta.type_name();
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
TokenStream::from(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause {
impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_clause {
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
#FQOption::Some(#FQClone::clone(<dyn #FQAny>::downcast_ref::<#type_name #ty_generics>(<dyn #bevy_reflect_path::Reflect>::as_any(reflect))?))
#FQOption::Some(#FQClone::clone(<dyn #FQAny>::downcast_ref::<#type_path #ty_generics>(<dyn #bevy_reflect_path::Reflect>::as_any(reflect))?))
}
}
})
@ -38,7 +38,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
let fqoption = FQOption.into_token_stream();
let type_name = reflect_enum.meta().type_name();
let enum_path = reflect_enum.meta().type_path();
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let ref_value = Ident::new("__param0", Span::call_site());
@ -47,8 +47,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
variant_constructors,
} = get_variant_constructors(reflect_enum, &ref_value, false);
let (impl_generics, ty_generics, where_clause) =
reflect_enum.meta().generics().split_for_impl();
let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl();
// Add FromReflect bound for each active field
let where_from_reflect_clause = extend_where_clause(
@ -58,11 +57,12 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
ignored_types: reflect_enum.ignored_types().into_boxed_slice(),
active_trait_bounds: quote!(#bevy_reflect_path::FromReflect),
ignored_trait_bounds: quote!(#FQDefault),
..WhereClauseOptions::type_path_bounds(reflect_enum.meta())
},
);
TokenStream::from(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_from_reflect_clause {
impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause {
fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) {
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
@ -90,8 +90,7 @@ impl MemberValuePair {
fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> TokenStream {
let fqoption = FQOption.into_token_stream();
let struct_name = reflect_struct.meta().type_name();
let generics = reflect_struct.meta().generics();
let struct_path = reflect_struct.meta().type_path();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let ref_struct = Ident::new("__ref_struct", Span::call_site());
@ -129,7 +128,11 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token
)
};
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
// Add FromReflect bound for each active field
let where_from_reflect_clause = extend_where_clause(
@ -143,11 +146,12 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token
} else {
quote!(#FQDefault)
},
..WhereClauseOptions::type_path_bounds(reflect_struct.meta())
},
);
TokenStream::from(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause
{
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = #bevy_reflect_path::Reflect::reflect_ref(reflect) {

View File

@ -1,7 +1,7 @@
use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructField};
use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors};
use crate::fq_std::{FQAny, FQBox, FQOption, FQResult};
use crate::impls::impl_typed;
use crate::impls::{impl_type_path, impl_typed};
use crate::utility::extend_where_clause;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
@ -10,7 +10,7 @@ use syn::Fields;
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let enum_name = reflect_enum.meta().type_name();
let enum_path = reflect_enum.meta().type_path();
let ref_name = Ident::new("__name_param", Span::call_site());
let ref_index = Ident::new("__index_param", Span::call_site());
@ -59,7 +59,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
}
});
let string_name = enum_name.to_string();
let string_name = enum_path.get_ident().unwrap().to_string();
#[cfg(feature = "documentation")]
let info_generator = {
@ -77,22 +77,23 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
};
let typed_impl = impl_typed(
enum_name,
reflect_enum.meta().generics(),
reflect_enum.meta(),
&where_clause_options,
quote! {
let variants = [#(#variant_info),*];
let info = #info_generator;
#bevy_reflect_path::TypeInfo::Enum(info)
},
bevy_reflect_path,
);
let type_path_impl = impl_type_path(reflect_enum.meta(), &where_clause_options);
let get_type_registration_impl = reflect_enum
.meta()
.get_type_registration(&where_clause_options);
let (impl_generics, ty_generics, where_clause) =
reflect_enum.meta().generics().split_for_impl();
reflect_enum.meta().type_path().generics().split_for_impl();
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
@ -101,7 +102,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
#typed_impl
impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_reflect_clause {
#type_path_impl
impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause {
fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
match self {
#(#enum_field,)*
@ -185,7 +188,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
}
}
impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_reflect_clause {
impl #impl_generics #bevy_reflect_path::Reflect for #enum_path #ty_generics #where_reflect_clause {
#[inline]
fn type_name(&self) -> &str {
::core::any::type_name::<Self>()
@ -196,6 +199,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath {
self
}
#[inline]
fn into_any(self: #FQBox<Self>) -> #FQBox<dyn #FQAny> {
self

View File

@ -7,5 +7,6 @@ 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 typed::impl_type_path;
pub(crate) use typed::impl_typed;
pub(crate) use values::impl_value;

View File

@ -1,5 +1,5 @@
use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult};
use crate::impls::impl_typed;
use crate::impls::{impl_type_path, impl_typed};
use crate::utility::{extend_where_clause, ident_or_index};
use crate::ReflectStruct;
use proc_macro::TokenStream;
@ -10,7 +10,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
let fqoption = FQOption.into_token_stream();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_name = reflect_struct.meta().type_name();
let struct_path = reflect_struct.meta().type_path();
let field_names = reflect_struct
.active_fields()
@ -64,7 +64,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
}
};
let string_name = struct_name.to_string();
let string_name = struct_path.get_ident().unwrap().to_string();
#[cfg(feature = "documentation")]
let info_generator = {
@ -83,20 +83,24 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
let where_clause_options = reflect_struct.where_clause_options();
let typed_impl = impl_typed(
struct_name,
reflect_struct.meta().generics(),
reflect_struct.meta(),
&where_clause_options,
quote! {
let fields = [#field_generator];
let info = #info_generator;
#bevy_reflect_path::TypeInfo::Struct(info)
},
bevy_reflect_path,
);
let type_path_impl = impl_type_path(reflect_struct.meta(), &where_clause_options);
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
let (impl_generics, ty_generics, where_clause) =
reflect_struct.meta().generics().split_for_impl();
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
@ -105,7 +109,9 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
#typed_impl
impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_reflect_clause {
#type_path_impl
impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
match name {
#(#field_names => #fqoption::Some(&self.#field_idents),)*
@ -157,7 +163,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
}
}
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause {
impl #impl_generics #bevy_reflect_path::Reflect for #struct_path #ty_generics #where_reflect_clause {
#[inline]
fn type_name(&self) -> &str {
::core::any::type_name::<Self>()
@ -168,6 +174,11 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath {
self
}
#[inline]
fn into_any(self: #FQBox<Self>) -> #FQBox<dyn #FQAny> {
self

View File

@ -1,5 +1,5 @@
use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult};
use crate::impls::impl_typed;
use crate::impls::{impl_type_path, impl_typed};
use crate::utility::extend_where_clause;
use crate::ReflectStruct;
use proc_macro::TokenStream;
@ -11,7 +11,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
let fqoption = FQOption.into_token_stream();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_name = reflect_struct.meta().type_name();
let struct_path = reflect_struct.meta().type_path();
let field_idents = reflect_struct
.active_fields()
@ -58,7 +58,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
}
};
let string_name = struct_name.to_string();
let string_name = struct_path.get_ident().unwrap().to_string();
#[cfg(feature = "documentation")]
let info_generator = {
@ -76,19 +76,22 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
};
let typed_impl = impl_typed(
struct_name,
reflect_struct.meta().generics(),
reflect_struct.meta(),
&where_clause_options,
quote! {
let fields = [#field_generator];
let info = #info_generator;
#bevy_reflect_path::TypeInfo::TupleStruct(info)
},
bevy_reflect_path,
);
let (impl_generics, ty_generics, where_clause) =
reflect_struct.meta().generics().split_for_impl();
let type_path_impl = impl_type_path(reflect_struct.meta(), &where_clause_options);
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
@ -97,7 +100,9 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
#typed_impl
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_reflect_clause {
#type_path_impl
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
match index {
#(#field_indices => #fqoption::Some(&self.#field_idents),)*
@ -128,7 +133,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
}
}
impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause {
impl #impl_generics #bevy_reflect_path::Reflect for #struct_path #ty_generics #where_reflect_clause {
#[inline]
fn type_name(&self) -> &str {
::core::any::type_name::<Self>()
@ -139,6 +144,11 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath {
self
}
#[inline]
fn into_any(self: #FQBox<Self>) -> #FQBox<dyn #FQAny> {
self

View File

@ -1,43 +1,149 @@
use crate::utility::{extend_where_clause, WhereClauseOptions};
use proc_macro2::Ident;
use quote::quote;
use syn::{Generics, Path};
use crate::utility::{extend_where_clause, StringExpr, WhereClauseOptions};
use quote::{quote, ToTokens};
#[allow(clippy::too_many_arguments)]
pub(crate) fn impl_typed(
type_name: &Ident,
generics: &Generics,
where_clause_options: &WhereClauseOptions,
use crate::{
derive_data::{ReflectMeta, ReflectTypePath},
utility::wrap_in_option,
};
/// Returns an expression for a `NonGenericTypeCell` or `GenericTypeCell` to generate `'static` references.
fn static_type_cell(
meta: &ReflectMeta,
property: TypedProperty,
generator: proc_macro2::TokenStream,
bevy_reflect_path: &Path,
) -> proc_macro2::TokenStream {
let is_generic = !generics.params.is_empty();
let bevy_reflect_path = meta.bevy_reflect_path();
if meta.type_path().impl_is_generic() {
let cell_type = match property {
TypedProperty::TypePath => quote!(GenericTypePathCell),
TypedProperty::TypeInfo => quote!(GenericTypeInfoCell),
};
let static_generator = if is_generic {
quote! {
static CELL: #bevy_reflect_path::utility::GenericTypeInfoCell = #bevy_reflect_path::utility::GenericTypeInfoCell::new();
static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
CELL.get_or_insert::<Self, _>(|| {
#generator
})
}
} else {
let cell_type = match property {
TypedProperty::TypePath => unreachable!(
"Cannot have a non-generic type path cell. Use string literals and core::concat instead."
),
TypedProperty::TypeInfo => quote!(NonGenericTypeInfoCell),
};
quote! {
static CELL: #bevy_reflect_path::utility::NonGenericTypeInfoCell = #bevy_reflect_path::utility::NonGenericTypeInfoCell::new();
static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
CELL.get_or_set(|| {
#generator
})
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum TypedProperty {
TypeInfo,
TypePath,
}
pub(crate) fn impl_type_path(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let (long_type_path, short_type_path) = if type_path.impl_is_generic() {
let long_path_cell = static_type_cell(
meta,
TypedProperty::TypePath,
type_path.long_type_path(bevy_reflect_path).into_owned(),
);
let short_path_cell = static_type_cell(
meta,
TypedProperty::TypePath,
type_path.short_type_path(bevy_reflect_path).into_owned(),
);
(
long_path_cell.to_token_stream(),
short_path_cell.to_token_stream(),
)
} else {
(
type_path.long_type_path(bevy_reflect_path).into_borrowed(),
type_path.short_type_path(bevy_reflect_path).into_borrowed(),
)
};
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let type_ident = wrap_in_option(type_path.type_ident().map(StringExpr::into_borrowed));
let module_path = wrap_in_option(type_path.module_path().map(StringExpr::into_borrowed));
let crate_name = wrap_in_option(type_path.crate_name().map(StringExpr::into_borrowed));
let primitive_assert = if let ReflectTypePath::Primitive(_) = type_path {
Some(quote! {
const _: () = {
mod private_scope {
// Compiles if it can be named when there are no imports.
type AssertIsPrimitive = #type_path;
}
};
})
} else {
None
};
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
// Add Typed bound for each active field
let where_reflect_clause = extend_where_clause(where_clause, where_clause_options);
quote! {
impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_reflect_clause {
fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
#static_generator
#primitive_assert
impl #impl_generics #bevy_reflect_path::TypePath for #type_path #ty_generics #where_reflect_clause {
fn type_path() -> &'static str {
#long_type_path
}
fn short_type_path() -> &'static str {
#short_type_path
}
fn type_ident() -> Option<&'static str> {
#type_ident
}
fn crate_name() -> Option<&'static str> {
#crate_name
}
fn module_path() -> Option<&'static str> {
#module_path
}
}
}
}
pub(crate) fn impl_typed(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
type_info_generator: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let type_info_cell = static_type_cell(meta, TypedProperty::TypeInfo, type_info_generator);
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = extend_where_clause(where_clause, where_clause_options);
quote! {
impl #impl_generics #bevy_reflect_path::Typed for #type_path #ty_generics #where_reflect_clause {
fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
#type_info_cell
}
}
}

View File

@ -1,6 +1,6 @@
use crate::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult};
use crate::impls::impl_typed;
use crate::utility::WhereClauseOptions;
use crate::impls::{impl_type_path, impl_typed};
use crate::utility::{extend_where_clause, WhereClauseOptions};
use crate::ReflectMeta;
use proc_macro::TokenStream;
use quote::quote;
@ -8,7 +8,7 @@ use quote::quote;
/// Implements `GetTypeRegistration` and `Reflect` for the given type data.
pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let type_name = meta.type_name();
let type_path = meta.type_path();
let hash_fn = meta.traits().get_hash_impl(bevy_reflect_path);
let partial_eq_fn = meta.traits().get_partial_eq_impl(bevy_reflect_path);
@ -22,27 +22,30 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
#[cfg(not(feature = "documentation"))]
let with_docs: Option<proc_macro2::TokenStream> = None;
let where_clause_options = WhereClauseOptions::default();
let where_clause_options = WhereClauseOptions::type_path_bounds(meta);
let typed_impl = impl_typed(
type_name,
meta.generics(),
meta,
&where_clause_options,
quote! {
let info = #bevy_reflect_path::ValueInfo::new::<Self>() #with_docs;
#bevy_reflect_path::TypeInfo::Value(info)
},
bevy_reflect_path,
);
let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl();
let type_path_impl = impl_type_path(meta, &where_clause_options);
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options);
let get_type_registration_impl = meta.get_type_registration(&where_clause_options);
TokenStream::from(quote! {
#get_type_registration_impl
#type_path_impl
#typed_impl
impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause {
impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause {
#[inline]
fn type_name(&self) -> &str {
::core::any::type_name::<Self>()
@ -53,6 +56,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath {
self
}
#[inline]
fn into_any(self: #FQBox<Self>) -> #FQBox<dyn #FQAny> {
self

View File

@ -26,20 +26,27 @@ 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 crate::type_uuid::gen_impl_type_uuid;
use container_attributes::ReflectTraits;
use derive_data::ReflectTypePath;
use proc_macro::TokenStream;
use quote::quote;
use reflect_value::ReflectValueDef;
use syn::spanned::Spanned;
use syn::{parse_macro_input, DeriveInput};
use type_path::NamedTypePathDef;
use type_uuid::TypeUuidDef;
use utility::WhereClauseOptions;
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";
pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name";
/// The main derive macro used by `bevy_reflect` for deriving its `Reflect` trait.
///
@ -52,6 +59,8 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
/// This macro comes with some helper attributes that can be added to the container item
/// in order to provide additional functionality or alter the generated implementations.
///
/// In addition to those listed, this macro can also use the attributes for [`TypePath`] derives.
///
/// ## `#[reflect(Ident)]`
///
/// The `#[reflect(Ident)]` attribute is used to add type data registrations to the `GetTypeRegistration`
@ -124,7 +133,7 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
/// which will be used by the reflection serializers to determine whether or not the field is serializable.
///
/// [`reflect_trait`]: macro@reflect_trait
#[proc_macro_derive(Reflect, attributes(reflect, reflect_value))]
#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, type_path, type_name))]
pub fn derive_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@ -188,6 +197,36 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
}
}
/// Derives the `TypePath` trait, providing a stable alternative to [`std::any::type_name`].
///
/// # Container Attributes
///
/// ## `#[type_path = "my_crate::foo"]`
///
/// Optionally specifies a custom module path to use instead of [`module_path`].
///
/// This path does not include the final identifier.
///
/// ## `#[type_name = "RenamedType"]`
///
/// Optionally specifies a new terminating identifier for `TypePath`.
///
/// To use this attribute, `#[type_path = "..."]` must also be specified.
#[proc_macro_derive(TypePath, attributes(type_path, type_name))]
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(),
};
impls::impl_type_path(
derive_data.meta(),
&WhereClauseOptions::type_path_bounds(derive_data.meta()),
)
.into()
}
// From https://github.com/randomPoison/type-uuid
#[proc_macro_derive(TypeUuid, attributes(uuid))]
pub fn derive_type_uuid(input: TokenStream) -> TokenStream {
@ -254,30 +293,47 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
/// The only reason for this macro's existence is so that `bevy_reflect` can easily implement the reflection traits
/// on primitives and other Rust types internally.
///
/// Since this macro also implements `TypePath`, the type path must be explicit.
/// See [`impl_type_path!`] for the exact syntax.
///
/// # Examples
///
/// Types can be passed with or without registering type data:
///
/// ```ignore
/// impl_reflect_value!(foo);
/// impl_reflect_value!(bar(Debug, Default, Serialize, Deserialize));
/// impl_reflect_value!(::my_crate::Foo);
/// impl_reflect_value!(::my_crate::Bar(Debug, Default, Serialize, Deserialize));
/// ```
///
/// Generic types can also specify their parameters and bounds:
///
/// ```ignore
/// impl_reflect_value!(foo<T1, T2: Baz> where T1: Bar (Default, Serialize, Deserialize));
/// impl_reflect_value!(::my_crate::Foo<T1, T2: Baz> where T1: Bar (Default, Serialize, Deserialize));
/// ```
///
/// Custom type paths can be specified:
///
/// ```ignore
/// impl_reflect_value!((in not_my_crate as NotFoo) Foo(Debug, Default));
/// ```
///
/// [deriving `Reflect`]: Reflect
#[proc_macro]
pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as ReflectValueDef);
let meta = ReflectMeta::new(
&def.type_name,
&def.generics,
def.traits.unwrap_or_default(),
);
let default_name = &def.type_path.segments.last().unwrap().ident;
let type_path = if def.type_path.leading_colon.is_none() && def.custom_path.is_none() {
ReflectTypePath::Primitive(default_name)
} else {
ReflectTypePath::External {
path: &def.type_path,
custom_path: def.custom_path.map(|path| path.into_path(default_name)),
generics: &def.generics,
}
};
let meta = ReflectMeta::new(type_path, def.traits.unwrap_or_default());
#[cfg(feature = "documentation")]
let meta = meta.with_docs(documentation::Documentation::from_attributes(&def.attrs));
@ -294,23 +350,27 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
/// which have greater functionality. The type being reflected must be in scope, as you cannot
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
///
/// It is necessary to add a `#[type_path = "my_crate::foo"]` attribute to all types.
///
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
/// error message and fail to compile. If the type does not implement `Default`, it may not
/// be possible to reflect without extending the macro.
///
///
/// # Example
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
/// ```ignore
/// use bevy::prelude::Vec3;
///
/// impl_reflect_struct!(
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// struct Vec3 {
/// x: f32,
/// y: f32,
/// z: f32
/// }
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// #[type_path = "bevy::prelude"]
/// struct Vec3 {
/// x: f32,
/// y: f32,
/// z: f32
/// }
/// );
/// ```
#[proc_macro]
@ -323,6 +383,15 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
match derive_data {
ReflectDerive::Struct(struct_data) => {
if !struct_data.meta().type_path().has_custom_path() {
return syn::Error::new(
struct_data.meta().type_path().span(),
format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using `impl_reflect_struct`")
)
.into_compile_error()
.into();
}
let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&struct_data).into();
let impl_from_struct: proc_macro2::TokenStream =
from_reflect::impl_struct(&struct_data).into();
@ -370,11 +439,84 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
#[proc_macro]
pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as ReflectValueDef);
from_reflect::impl_value(&ReflectMeta::new(
&def.type_name,
&def.generics,
def.traits.unwrap_or_default(),
))
let default_name = &def.type_path.segments.last().unwrap().ident;
let type_path = if def.type_path.leading_colon.is_none()
&& def.custom_path.is_none()
&& def.generics.params.is_empty()
{
ReflectTypePath::Primitive(default_name)
} else {
ReflectTypePath::External {
path: &def.type_path,
custom_path: def.custom_path.map(|alias| alias.into_path(default_name)),
generics: &def.generics,
}
};
from_reflect::impl_value(&ReflectMeta::new(type_path, def.traits.unwrap_or_default()))
}
/// A replacement for [deriving `TypePath`] for use on foreign types.
///
/// Since (unlike the derive) this macro may be invoked in a different module to where the type is defined,
/// it requires an 'absolute' path definition.
///
/// Specifically, a leading `::` denoting a global path must be specified
/// or a preceeding `(in my_crate::foo)` to specify the custom path must be used.
///
/// # Examples
///
/// Implementing `TypePath` on a foreign type:
/// ```rust,ignore
/// impl_type_path!(::foreign_crate::foo::bar::Baz);
/// ```
///
/// On a generic type:
/// ```rust,ignore
/// impl_type_path!(::foreign_crate::Foo<T>);
/// ```
///
/// On a primitive (note this will not compile for a non-primitive type):
/// ```rust,ignore
/// impl_type_path!(bool);
/// ```
///
/// With a custom type path:
/// ```rust,ignore
/// impl_type_path!((in other_crate::foo::bar) Baz);
/// ```
///
/// With a custom type path and a custom type name:
/// ```rust,ignore
/// impl_type_path!((in other_crate::foo as Baz) Bar);
/// ```
///
/// [deriving `TypePath`]: TypePath
#[proc_macro]
pub fn impl_type_path(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as NamedTypePathDef);
let type_path = match def {
NamedTypePathDef::External {
ref path,
custom_path,
ref generics,
} => {
let default_name = &path.segments.last().unwrap().ident;
ReflectTypePath::External {
path,
custom_path: custom_path.map(|path| path.into_path(default_name)),
generics,
}
}
NamedTypePathDef::Primtive(ref ident) => ReflectTypePath::Primitive(ident),
};
let meta = ReflectMeta::new(type_path, ReflectTraits::default());
impls::impl_type_path(&meta, &WhereClauseOptions::type_path_bounds(&meta)).into()
}
/// Derives `TypeUuid` for the given type. This is used internally to implement `TypeUuid` on foreign types, such as those in the std. This macro should be used in the format of `<[Generic Params]> [Type (Path)], [Uuid (String Literal)]`.

View File

@ -1,58 +1,59 @@
use crate::container_attributes::ReflectTraits;
use proc_macro2::Ident;
use crate::type_path::CustomPathDef;
use syn::parse::{Parse, ParseStream};
use syn::token::{Paren, Where};
use syn::{parenthesized, Attribute, Generics};
use syn::token::Paren;
use syn::{parenthesized, Attribute, Generics, Path};
/// A struct used to define a simple reflected value type (such as primitives).
///
///
///
/// This takes the form:
///
/// ```ignore
/// // Standard
/// foo(TraitA, TraitB)
/// ::my_crate::foo::Bar(TraitA, TraitB)
///
/// // With generics
/// foo<T1: Bar, T2>(TraitA, TraitB)
/// ::my_crate::foo::Bar<T1: Bar, T2>(TraitA, TraitB)
///
/// // With generics and where clause
/// foo<T1, T2> where T1: Bar (TraitA, TraitB)
/// ::my_crate::foo::Bar<T1, T2> where T1: Bar (TraitA, TraitB)
///
/// // With a custom path (not with impl_from_reflect_value)
/// (in my_crate::bar) Bar(TraitA, TraitB)
/// ```
pub(crate) struct ReflectValueDef {
#[allow(dead_code)]
pub attrs: Vec<Attribute>,
pub type_name: Ident,
pub type_path: Path,
pub generics: Generics,
pub traits: Option<ReflectTraits>,
pub custom_path: Option<CustomPathDef>,
}
impl Parse for ReflectValueDef {
fn parse(input: ParseStream) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let type_ident = input.parse::<Ident>()?;
let generics = input.parse::<Generics>()?;
let mut lookahead = input.lookahead1();
let mut where_clause = None;
if lookahead.peek(Where) {
where_clause = Some(input.parse()?);
lookahead = input.lookahead1();
}
let custom_path = CustomPathDef::parse_parenthesized(input)?;
let type_path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
let mut traits = None;
if lookahead.peek(Paren) {
if input.peek(Paren) {
let content;
parenthesized!(content in input);
traits = Some(content.parse::<ReflectTraits>()?);
}
Ok(ReflectValueDef {
attrs,
type_name: type_ident,
generics: Generics {
where_clause,
..generics
},
type_path,
generics,
traits,
custom_path,
})
}
}

View File

@ -2,21 +2,21 @@
use crate::utility::{extend_where_clause, WhereClauseOptions};
use bit_set::BitSet;
use proc_macro2::Ident;
use quote::quote;
use syn::{Generics, Path};
use crate::derive_data::ReflectMeta;
/// Creates the `GetTypeRegistration` impl for the given type data.
#[allow(clippy::too_many_arguments)]
pub(crate) fn impl_get_type_registration(
type_name: &Ident,
bevy_reflect_path: &Path,
registration_data: &[Ident],
generics: &Generics,
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
serialization_denylist: Option<&BitSet<u32>>,
) -> proc_macro2::TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let registration_data = meta.traits().idents();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let serialization_data = serialization_denylist.map(|denylist| {
let denylist = denylist.into_iter();
quote! {
@ -29,12 +29,12 @@ pub(crate) fn impl_get_type_registration(
quote! {
#[allow(unused_mut)]
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_clause {
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_path #ty_generics #where_reflect_clause {
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
let mut registration = #bevy_reflect_path::TypeRegistration::of::<Self>();
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<Self>::from_type());
#serialization_data
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<Self>::from_type());)*
registration
}
}

View File

@ -0,0 +1,102 @@
use proc_macro2::Ident;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
token::Paren,
Generics, Path, PathSegment, Token,
};
pub(crate) fn parse_path_no_leading_colon(input: ParseStream) -> syn::Result<Path> {
if input.peek(Token![::]) {
return Err(input.error("did not expect a leading double colon (`::`)"));
}
let path = Path::parse_mod_style(input)?;
if path.segments.is_empty() {
Err(input.error("expected a path"))
} else {
Ok(path)
}
}
/// An alias for a `TypePath`.
///
/// This is the parenthesized part of `(in my_crate::foo as MyType) SomeType`.
pub(crate) struct CustomPathDef {
path: Path,
name: Option<Ident>,
}
impl CustomPathDef {
pub fn into_path(mut self, default_name: &Ident) -> Path {
let name = PathSegment::from(self.name.unwrap_or_else(|| default_name.clone()));
self.path.segments.push(name);
self.path
}
pub fn parse_parenthesized(input: ParseStream) -> syn::Result<Option<Self>> {
if input.peek(Paren) {
let path;
parenthesized!(path in input);
Ok(Some(path.call(Self::parse)?))
} else {
Ok(None)
}
}
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse::<Token![in]>()?;
let custom_path = parse_path_no_leading_colon(input)?;
if !input.peek(Token![as]) {
return Ok(Self {
path: custom_path,
name: None,
});
}
input.parse::<Token![as]>()?;
let custom_name: Ident = input.parse()?;
Ok(Self {
path: custom_path,
name: Some(custom_name),
})
}
}
pub(crate) enum NamedTypePathDef {
External {
path: Path,
generics: Generics,
custom_path: Option<CustomPathDef>,
},
Primtive(Ident),
}
impl Parse for NamedTypePathDef {
fn parse(input: ParseStream) -> syn::Result<Self> {
let custom_path = CustomPathDef::parse_parenthesized(input)?;
let path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
if path.leading_colon.is_none() && custom_path.is_none() {
if path.segments.len() == 1 {
let ident = path.segments.into_iter().next().unwrap().ident;
Ok(NamedTypePathDef::Primtive(ident))
} else {
Err(input.error("non-customized paths must start with a double colon (`::`)"))
}
} else {
Ok(NamedTypePathDef::External {
path,
generics,
custom_path,
})
}
}
}

View File

@ -1,11 +1,11 @@
//! General-purpose utility functions for internal usage within this crate.
use crate::field_attributes::ReflectIgnoreBehavior;
use crate::{derive_data::ReflectMeta, field_attributes::ReflectIgnoreBehavior, fq_std::FQOption};
use bevy_macro_utils::BevyManifest;
use bit_set::BitSet;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{Member, Path, Type, WhereClause};
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, LitStr, Member, Path, Type, WhereClause};
/// Returns the correct path for `bevy_reflect`.
pub(crate) fn get_bevy_reflect_path() -> Path {
@ -60,6 +60,10 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member {
/// Options defining how to extend the `where` clause in reflection with any additional bounds needed.
pub(crate) struct WhereClauseOptions {
/// Type parameters that need extra trait bounds.
pub(crate) parameter_types: Box<[Ident]>,
/// Trait bounds to add to the type parameters.
pub(crate) parameter_trait_bounds: proc_macro2::TokenStream,
/// Any types that will be reflected and need an extra trait bound
pub(crate) active_types: Box<[Type]>,
/// Trait bounds to add to the active types
@ -70,12 +74,31 @@ pub(crate) struct WhereClauseOptions {
pub(crate) ignored_trait_bounds: proc_macro2::TokenStream,
}
impl WhereClauseOptions {
/// Extends a where clause, adding a `TypePath` bound to each type parameter.
pub fn type_path_bounds(meta: &ReflectMeta) -> Self {
let bevy_reflect_path = meta.bevy_reflect_path();
Self {
parameter_types: meta
.type_path()
.generics()
.type_params()
.map(|ty| ty.ident.clone())
.collect(),
parameter_trait_bounds: quote! { #bevy_reflect_path::TypePath },
..Default::default()
}
}
}
impl Default for WhereClauseOptions {
/// By default, don't add any additional bounds to the `where` clause
fn default() -> Self {
Self {
parameter_types: Box::new([]),
active_types: Box::new([]),
ignored_types: Box::new([]),
parameter_trait_bounds: quote! {},
active_trait_bounds: quote! {},
ignored_trait_bounds: quote! {},
}
@ -117,22 +140,26 @@ pub(crate) fn extend_where_clause(
where_clause: Option<&WhereClause>,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let parameter_types = &where_clause_options.parameter_types;
let active_types = &where_clause_options.active_types;
let ignored_types = &where_clause_options.ignored_types;
let parameter_trait_bounds = &where_clause_options.parameter_trait_bounds;
let active_trait_bounds = &where_clause_options.active_trait_bounds;
let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds;
let mut generic_where_clause = if let Some(where_clause) = where_clause {
let predicates = where_clause.predicates.iter();
quote! {where #(#predicates,)*}
} else if !(active_types.is_empty() && ignored_types.is_empty()) {
} else if !(parameter_types.is_empty() && active_types.is_empty() && ignored_types.is_empty()) {
quote! {where}
} else {
quote! {}
quote!()
};
generic_where_clause.extend(quote! {
#(#active_types: #active_trait_bounds,)*
#(#ignored_types: #ignored_trait_bounds,)*
// Leave parameter bounds to the end for more sane error messages.
#(#parameter_types: #parameter_trait_bounds,)*
});
generic_where_clause
}
@ -212,3 +239,118 @@ where
bitset
}
/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
pub(crate) fn wrap_in_option(tokens: Option<proc_macro2::TokenStream>) -> proc_macro2::TokenStream {
match tokens {
Some(tokens) => quote! {
#FQOption::Some(#tokens)
},
None => quote! {
#FQOption::None
},
}
}
/// Contains tokens representing different kinds of string.
#[derive(Clone)]
pub(crate) enum StringExpr {
/// A string that is valid at compile time.
///
/// This is either a string literal like `"mystring"`,
/// or a string created by a macro like [`module_path`]
/// or [`concat`].
Const(proc_macro2::TokenStream),
/// A [string slice](str) that is borrowed for a `'static` lifetime.
Borrowed(proc_macro2::TokenStream),
/// An [owned string](String).
Owned(proc_macro2::TokenStream),
}
impl<T: ToString + Spanned> From<T> for StringExpr {
fn from(value: T) -> Self {
Self::from_lit(&LitStr::new(&value.to_string(), value.span()))
}
}
impl StringExpr {
/// Creates a [constant] [`StringExpr`] from a [`struct@LitStr`].
///
/// [constant]: StringExpr::Const
pub fn from_lit(lit: &LitStr) -> Self {
Self::Const(lit.to_token_stream())
}
/// Creates a [constant] [`StringExpr`] by interpreting a [string slice][str] as a [`struct@LitStr`].
///
/// [constant]: StringExpr::Const
pub fn from_str(string: &str) -> Self {
Self::Const(string.into_token_stream())
}
/// Returns tokens for an [owned string](String).
///
/// The returned expression will allocate unless the [`StringExpr`] is [already owned].
///
/// [already owned]: StringExpr::Owned
pub fn into_owned(self) -> proc_macro2::TokenStream {
match self {
Self::Const(tokens) | Self::Borrowed(tokens) => quote! {
::std::string::ToString::to_string(#tokens)
},
Self::Owned(owned) => owned,
}
}
/// Returns tokens for a statically borrowed [string slice](str).
pub fn into_borrowed(self) -> proc_macro2::TokenStream {
match self {
Self::Const(tokens) | Self::Borrowed(tokens) => tokens,
Self::Owned(owned) => quote! {
&#owned
},
}
}
/// Appends a [`StringExpr`] to another.
///
/// If both expressions are [`StringExpr::Const`] this will use [`concat`] to merge them.
pub fn appended_by(mut self, other: StringExpr) -> Self {
if let Self::Const(tokens) = self {
if let Self::Const(more) = other {
return Self::Const(quote! {
::core::concat!(#tokens, #more)
});
}
self = Self::Const(tokens);
}
let owned = self.into_owned();
let borrowed = other.into_borrowed();
Self::Owned(quote! {
#owned + #borrowed
})
}
}
impl Default for StringExpr {
fn default() -> Self {
StringExpr::from_str("")
}
}
impl FromIterator<StringExpr> for StringExpr {
fn from_iter<T: IntoIterator<Item = StringExpr>>(iter: T) -> Self {
let mut iter = iter.into_iter();
match iter.next() {
Some(mut expr) => {
for next in iter {
expr = expr.appended_by(next);
}
expr
}
None => Default::default(),
}
}
}

View File

@ -1,4 +1,9 @@
use crate::{utility::reflect_hasher, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo};
use bevy_reflect_derive::impl_type_path;
use crate::{
self as bevy_reflect, utility::reflect_hasher, DynamicTypePath, Reflect, ReflectMut,
ReflectOwned, ReflectRef, TypeInfo,
};
use std::{
any::{Any, TypeId},
fmt::Debug,
@ -221,6 +226,11 @@ impl Reflect for DynamicArray {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -335,6 +345,7 @@ impl Array for DynamicArray {
}
}
impl_type_path!((in bevy_reflect) DynamicArray);
/// An iterator over an [`Array`].
pub struct ArrayIter<'a> {
array: &'a dyn Array,

View File

@ -1,6 +1,9 @@
use bevy_reflect_derive::impl_type_path;
use crate::{
enum_debug, enum_hash, enum_partial_eq, DynamicStruct, DynamicTuple, Enum, Reflect, ReflectMut,
ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo, VariantFieldIter, VariantType,
self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, DynamicStruct, DynamicTuple,
DynamicTypePath, Enum, Reflect, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo,
VariantFieldIter, VariantType,
};
use std::any::Any;
use std::fmt::Formatter;
@ -297,6 +300,11 @@ impl Reflect for DynamicEnum {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -420,3 +428,5 @@ impl Reflect for DynamicEnum {
write!(f, ")")
}
}
impl_type_path!((in bevy_reflect) DynamicEnum);

View File

@ -6,6 +6,7 @@ use glam::*;
impl_reflect_struct!(
#[reflect(Debug, Hash, PartialEq, Default)]
#[type_path = "glam"]
struct IVec2 {
x: i32,
y: i32,
@ -13,6 +14,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, Hash, PartialEq, Default)]
#[type_path = "glam"]
struct IVec3 {
x: i32,
y: i32,
@ -21,6 +23,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, Hash, PartialEq, Default)]
#[type_path = "glam"]
struct IVec4 {
x: i32,
y: i32,
@ -31,6 +34,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, Hash, PartialEq, Default)]
#[type_path = "glam"]
struct UVec2 {
x: u32,
y: u32,
@ -38,6 +42,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, Hash, PartialEq, Default)]
#[type_path = "glam"]
struct UVec3 {
x: u32,
y: u32,
@ -46,6 +51,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, Hash, PartialEq, Default)]
#[type_path = "glam"]
struct UVec4 {
x: u32,
y: u32,
@ -53,9 +59,9 @@ impl_reflect_struct!(
w: u32,
}
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Vec2 {
x: f32,
y: f32,
@ -63,6 +69,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Vec3 {
x: f32,
y: f32,
@ -71,6 +78,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Vec3A {
x: f32,
y: f32,
@ -79,6 +87,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Vec4 {
x: f32,
y: f32,
@ -89,6 +98,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct BVec2 {
x: bool,
y: bool,
@ -96,6 +106,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct BVec3 {
x: bool,
y: bool,
@ -104,6 +115,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct BVec4 {
x: bool,
y: bool,
@ -114,6 +126,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DVec2 {
x: f64,
y: f64,
@ -121,6 +134,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DVec3 {
x: f64,
y: f64,
@ -129,6 +143,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DVec4 {
x: f64,
y: f64,
@ -139,6 +154,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Mat2 {
x_axis: Vec2,
y_axis: Vec2,
@ -146,6 +162,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Mat3 {
x_axis: Vec3,
y_axis: Vec3,
@ -154,6 +171,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Mat3A {
x_axis: Vec3A,
y_axis: Vec3A,
@ -162,6 +180,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Mat4 {
x_axis: Vec4,
y_axis: Vec4,
@ -172,6 +191,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DMat2 {
x_axis: DVec2,
y_axis: DVec2,
@ -179,6 +199,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DMat3 {
x_axis: DVec3,
y_axis: DVec3,
@ -187,6 +208,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DMat4 {
x_axis: DVec4,
y_axis: DVec4,
@ -197,6 +219,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Affine2 {
matrix2: Mat2,
translation: Vec2,
@ -204,6 +227,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct Affine3A {
matrix3: Mat3A,
translation: Vec3A,
@ -212,6 +236,7 @@ impl_reflect_struct!(
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DAffine2 {
matrix2: DMat2,
translation: DVec2,
@ -219,6 +244,7 @@ impl_reflect_struct!(
);
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Default)]
#[type_path = "glam"]
struct DAffine3 {
matrix3: DMat3,
translation: DVec3,
@ -229,12 +255,24 @@ 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!(BVec3A(Debug, Default));
impl_reflect_value!(BVec4A(Debug, Default));
impl_reflect_value!(::glam::EulerRot(Debug, Default));
impl_reflect_value!(::glam::BVec3A(Debug, Default));
impl_reflect_value!(::glam::BVec4A(Debug, Default));

View File

@ -6,6 +6,7 @@ use bevy_reflect_derive::impl_reflect_struct;
impl_reflect_struct!(
#[reflect(Debug, PartialEq, Serialize, Deserialize, Default)]
#[type_path = "bevy_math"]
struct Rect {
min: Vec2,
max: Vec2,

View File

@ -1,13 +1,15 @@
use bevy_reflect_derive::impl_type_path;
use smallvec::SmallVec;
use std::any::Any;
use crate::utility::GenericTypeInfoCell;
use crate::{
FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter, Reflect, ReflectFromPtr,
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed,
self as bevy_reflect, DynamicTypePath, FromReflect, FromType, GetTypeRegistration, List,
ListInfo, ListIter, Reflect, ReflectFromPtr, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
TypePath, TypeRegistration, Typed,
};
impl<T: smallvec::Array + Send + Sync + 'static> List for SmallVec<T>
impl<T: smallvec::Array + TypePath + Send + Sync> List for SmallVec<T>
where
T::Item: FromReflect,
{
@ -74,7 +76,7 @@ where
}
}
impl<T: smallvec::Array + Send + Sync + 'static> Reflect for SmallVec<T>
impl<T: smallvec::Array + TypePath + Send + Sync> Reflect for SmallVec<T>
where
T::Item: FromReflect,
{
@ -86,6 +88,11 @@ where
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -140,7 +147,7 @@ where
}
}
impl<T: smallvec::Array + Send + Sync + 'static> Typed for SmallVec<T>
impl<T: smallvec::Array + TypePath + Send + Sync + 'static> Typed for SmallVec<T>
where
T::Item: FromReflect,
{
@ -150,7 +157,9 @@ where
}
}
impl<T: smallvec::Array + Send + Sync + 'static> FromReflect for SmallVec<T>
impl_type_path!(::smallvec::SmallVec<T: smallvec::Array + TypePath + Send + Sync>);
impl<T: smallvec::Array + TypePath + Send + Sync> FromReflect for SmallVec<T>
where
T::Item: FromReflect,
{
@ -167,7 +176,7 @@ where
}
}
impl<T: smallvec::Array + Send + Sync + 'static> GetTypeRegistration for SmallVec<T>
impl<T: smallvec::Array + TypePath + Send + Sync> GetTypeRegistration for SmallVec<T>
where
T::Item: FromReflect,
{

View File

@ -1,14 +1,16 @@
use crate::std_traits::ReflectDefault;
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, ReflectOwned};
use crate::{
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum,
EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter,
Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TupleVariantInfo,
TypeInfo, TypeRegistration, Typed, UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter,
VariantInfo, VariantType,
impl_type_path, map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum,
DynamicMap, DynamicTypePath, Enum, EnumInfo, FromReflect, FromType, GetTypeRegistration, List,
ListInfo, ListIter, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef,
ReflectSerialize, TupleVariantInfo, TypeInfo, TypePath, TypeRegistration, Typed,
UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, VariantInfo, VariantType,
};
use crate::utility::{reflect_hasher, GenericTypeInfoCell, NonGenericTypeInfoCell};
use crate::utility::{
reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell,
};
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use bevy_utils::HashSet;
use bevy_utils::{Duration, Instant};
@ -93,7 +95,7 @@ impl_reflect_value!(String(
Deserialize,
Default
));
impl_reflect_value!(PathBuf(
impl_reflect_value!(::std::path::PathBuf(
Debug,
Hash,
PartialEq,
@ -101,15 +103,18 @@ impl_reflect_value!(PathBuf(
Deserialize,
Default
));
impl_reflect_value!(Result<T: Clone + Reflect + 'static, E: Clone + Reflect + 'static>());
impl_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>());
impl_reflect_value!(Range<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeInclusive<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeFrom<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeTo<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeToInclusive<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeFull());
impl_reflect_value!(Duration(
impl_reflect_value!(
::core::result::Result < T: Clone + Reflect + TypePath,
E: Clone + Reflect + TypePath > ()
);
impl_reflect_value!(::bevy_utils::HashSet<T: Hash + Eq + Clone + Send + Sync>());
impl_reflect_value!(::core::ops::Range<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeInclusive<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeFrom<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeTo<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeToInclusive<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeFull());
impl_reflect_value!(::bevy_utils::Duration(
Debug,
Hash,
PartialEq,
@ -117,26 +122,104 @@ impl_reflect_value!(Duration(
Deserialize,
Default
));
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!(::bevy_utils::Instant(Debug, Hash, PartialEq));
impl_reflect_value!(::core::num::NonZeroI128(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroU128(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroIsize(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroUsize(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroI64(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroU64(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroU32(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroI32(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroI16(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroU16(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroU8(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
impl_reflect_value!(::core::num::NonZeroI8(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
// `Serialize` and `Deserialize` only for platforms supported by 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::ffi::OsString(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize
));
#[cfg(not(any(unix, windows)))]
impl_reflect_value!(OsString(Debug, Hash, PartialEq));
impl_reflect_value!(::std::ffi::OsString(Debug, Hash, PartialEq));
impl_from_reflect_value!(bool);
impl_from_reflect_value!(char);
@ -157,12 +240,12 @@ impl_from_reflect_value!(f64);
impl_from_reflect_value!(String);
impl_from_reflect_value!(PathBuf);
impl_from_reflect_value!(OsString);
impl_from_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>);
impl_from_reflect_value!(Range<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeInclusive<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeFrom<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeTo<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeToInclusive<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(HashSet<T: TypePath + Hash + Eq + Clone + Send + Sync>);
impl_from_reflect_value!(Range<T: TypePath + Clone + Send + Sync>);
impl_from_reflect_value!(RangeInclusive<T: TypePath + Clone + Send + Sync>);
impl_from_reflect_value!(RangeFrom<T: TypePath + Clone + Send + Sync>);
impl_from_reflect_value!(RangeTo<T: TypePath + Clone + Send + Sync>);
impl_from_reflect_value!(RangeToInclusive<T: TypePath + Clone + Send + Sync>);
impl_from_reflect_value!(RangeFull);
impl_from_reflect_value!(Duration);
impl_from_reflect_value!(Instant);
@ -180,8 +263,8 @@ impl_from_reflect_value!(NonZeroU8);
impl_from_reflect_value!(NonZeroI8);
macro_rules! impl_reflect_for_veclike {
($ty:ty, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => {
impl<T: FromReflect> List for $ty {
($ty:path, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => {
impl<T: FromReflect + TypePath> List for $ty {
#[inline]
fn get(&self, index: usize) -> Option<&dyn Reflect> {
<$sub>::get(self, index).map(|value| value as &dyn Reflect)
@ -228,8 +311,8 @@ macro_rules! impl_reflect_for_veclike {
}
#[inline]
fn iter(&self) -> $crate::ListIter {
$crate::ListIter::new(self)
fn iter(&self) -> ListIter {
ListIter::new(self)
}
#[inline]
@ -240,7 +323,7 @@ macro_rules! impl_reflect_for_veclike {
}
}
impl<T: FromReflect> Reflect for $ty {
impl<T: FromReflect + TypePath> Reflect for $ty {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
@ -249,6 +332,11 @@ macro_rules! impl_reflect_for_veclike {
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -307,14 +395,16 @@ macro_rules! impl_reflect_for_veclike {
}
}
impl<T: FromReflect> Typed for $ty {
impl<T: FromReflect + TypePath> Typed for $ty {
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| TypeInfo::List(ListInfo::new::<Self, T>()))
}
}
impl<T: FromReflect> GetTypeRegistration for $ty {
impl_type_path!($ty where T: FromReflect);
impl<T: FromReflect + TypePath> GetTypeRegistration for $ty {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<$ty>();
registration.insert::<ReflectFromPtr>(FromType::<$ty>::from_type());
@ -322,7 +412,7 @@ macro_rules! impl_reflect_for_veclike {
}
}
impl<T: FromReflect> FromReflect for $ty {
impl<T: FromReflect + TypePath> FromReflect for $ty {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
let mut new_list = Self::with_capacity(ref_list.len());
@ -338,9 +428,16 @@ macro_rules! impl_reflect_for_veclike {
};
}
impl_reflect_for_veclike!(Vec<T>, Vec::insert, Vec::remove, Vec::push, Vec::pop, [T]);
impl_reflect_for_veclike!(
VecDeque<T>,
::alloc::vec::Vec<T>,
Vec::insert,
Vec::remove,
Vec::push,
Vec::pop,
[T]
);
impl_reflect_for_veclike!(
::alloc::collections::VecDeque<T>,
VecDeque::insert,
VecDeque::remove,
VecDeque::push_back,
@ -349,12 +446,12 @@ impl_reflect_for_veclike!(
);
macro_rules! impl_reflect_for_hashmap {
($ty:ty) => {
($ty:path) => {
impl<K, V, S> Map for $ty
where
K: FromReflect + Eq + Hash,
V: FromReflect,
S: BuildHasher + Send + Sync + 'static,
K: FromReflect + TypePath + Eq + Hash,
V: FromReflect + TypePath,
S: TypePath + BuildHasher + Send + Sync,
{
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
key.downcast_ref::<K>()
@ -446,9 +543,9 @@ macro_rules! impl_reflect_for_hashmap {
impl<K, V, S> Reflect for $ty
where
K: FromReflect + Eq + Hash,
V: FromReflect,
S: BuildHasher + Send + Sync + 'static,
K: FromReflect + TypePath + Eq + Hash,
V: FromReflect + TypePath,
S: TypePath + BuildHasher + Send + Sync,
{
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
@ -458,6 +555,10 @@ macro_rules! impl_reflect_for_hashmap {
Some(<Self as Typed>::type_info())
}
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -515,9 +616,9 @@ macro_rules! impl_reflect_for_hashmap {
impl<K, V, S> Typed for $ty
where
K: FromReflect + Eq + Hash,
V: FromReflect,
S: BuildHasher + Send + Sync + 'static,
K: FromReflect + TypePath + Eq + Hash,
V: FromReflect + TypePath,
S: TypePath + BuildHasher + Send + Sync,
{
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
@ -527,9 +628,9 @@ macro_rules! impl_reflect_for_hashmap {
impl<K, V, S> GetTypeRegistration for $ty
where
K: FromReflect + Eq + Hash,
V: FromReflect,
S: BuildHasher + Send + Sync + 'static,
K: FromReflect + TypePath + Eq + Hash,
V: FromReflect + TypePath,
S: TypePath + BuildHasher + Send + Sync,
{
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
@ -540,9 +641,9 @@ macro_rules! impl_reflect_for_hashmap {
impl<K, V, S> FromReflect for $ty
where
K: FromReflect + Eq + Hash,
V: FromReflect,
S: BuildHasher + Default + Send + Sync + 'static,
K: FromReflect + TypePath + Eq + Hash,
V: FromReflect + TypePath,
S: TypePath + BuildHasher + Default + Send + Sync,
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Map(ref_map) = reflect.reflect_ref() {
@ -561,10 +662,27 @@ macro_rules! impl_reflect_for_hashmap {
};
}
impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap<K, V, S>);
impl_reflect_for_hashmap!(std::collections::HashMap<K, V, S>);
impl_reflect_for_hashmap!(::std::collections::HashMap<K, V, S>);
impl_type_path!(::std::collections::hash_map::RandomState);
impl_type_path!(
::std::collections::HashMap<K, V, S>
where
K: FromReflect + Eq + Hash + ?Sized,
V: FromReflect + ?Sized,
S: BuildHasher + Send + Sync + 'static,
);
impl<T: Reflect, const N: usize> Array for [T; N] {
impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap<K, V, S>);
impl_type_path!(::bevy_utils::hashbrown::hash_map::DefaultHashBuilder);
impl_type_path!(
::bevy_utils::hashbrown::HashMap<K, V, S>
where
K: FromReflect + Eq + Hash + ?Sized,
V: FromReflect + ?Sized,
S: BuildHasher + Send + Sync + 'static,
);
impl<T: Reflect + TypePath, const N: usize> Array for [T; N] {
#[inline]
fn get(&self, index: usize) -> Option<&dyn Reflect> {
<[T]>::get(self, index).map(|value| value as &dyn Reflect)
@ -593,7 +711,7 @@ impl<T: Reflect, const N: usize> Array for [T; N] {
}
}
impl<T: Reflect, const N: usize> Reflect for [T; N] {
impl<T: Reflect + TypePath, const N: usize> Reflect for [T; N] {
#[inline]
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
@ -603,6 +721,11 @@ impl<T: Reflect, const N: usize> Reflect for [T; N] {
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -675,7 +798,7 @@ impl<T: Reflect, const N: usize> Reflect for [T; N] {
}
}
impl<T: FromReflect, const N: usize> FromReflect for [T; N] {
impl<T: FromReflect + TypePath, const N: usize> FromReflect for [T; N] {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Array(ref_array) = reflect.reflect_ref() {
let mut temp_vec = Vec::with_capacity(ref_array.len());
@ -689,13 +812,25 @@ impl<T: FromReflect, const N: usize> FromReflect for [T; N] {
}
}
impl<T: Reflect, const N: usize> Typed for [T; N] {
impl<T: Reflect + TypePath, const N: usize> Typed for [T; N] {
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| TypeInfo::Array(ArrayInfo::new::<Self, T>(N)))
}
}
impl<T: Reflect + TypePath, const N: usize> TypePath for [T; N] {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("[{t}; {N}]", t = T::type_path()))
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| format!("[{t}; {N}]", t = T::short_type_path()))
}
}
// TODO:
// `FromType::from_type` requires `Deserialize<'de>` to be implemented for `T`.
// Currently serde only supports `Deserialize<'de>` for arrays up to size 32.
@ -704,7 +839,7 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
macro_rules! impl_array_get_type_registration {
($($N:expr)+) => {
$(
impl<T: Reflect > GetTypeRegistration for [T; $N] {
impl<T: Reflect + TypePath> GetTypeRegistration for [T; $N] {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<[T; $N]>()
}
@ -720,13 +855,13 @@ impl_array_get_type_registration! {
30 31 32
}
impl<T: FromReflect> GetTypeRegistration for Option<T> {
impl<T: FromReflect + TypePath> GetTypeRegistration for Option<T> {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<Option<T>>()
}
}
impl<T: FromReflect> Enum for Option<T> {
impl<T: FromReflect + TypePath> Enum for Option<T> {
fn field(&self, _name: &str) -> Option<&dyn Reflect> {
None
}
@ -797,7 +932,7 @@ impl<T: FromReflect> Enum for Option<T> {
}
}
impl<T: FromReflect> Reflect for Option<T> {
impl<T: FromReflect + TypePath> Reflect for Option<T> {
#[inline]
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
@ -808,6 +943,11 @@ impl<T: FromReflect> Reflect for Option<T> {
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -911,7 +1051,7 @@ impl<T: FromReflect> Reflect for Option<T> {
}
}
impl<T: FromReflect> FromReflect for Option<T> {
impl<T: FromReflect + TypePath> FromReflect for Option<T> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() {
match dyn_enum.variant_name() {
@ -949,7 +1089,7 @@ impl<T: FromReflect> FromReflect for Option<T> {
}
}
impl<T: FromReflect> Typed for Option<T> {
impl<T: FromReflect + TypePath> Typed for Option<T> {
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| {
@ -964,6 +1104,8 @@ impl<T: FromReflect> Typed for Option<T> {
}
}
impl_type_path!(::core::option::Option<T: FromReflect + TypePath>);
impl Reflect for Cow<'static, str> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
@ -973,6 +1115,11 @@ impl Reflect for Cow<'static, str> {
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -1051,6 +1198,30 @@ impl Typed for Cow<'static, str> {
}
}
impl TypePath for Cow<'static, str> {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "std::borrow::Cow::<str>".to_owned())
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "Cow<str>".to_owned())
}
fn type_ident() -> Option<&'static str> {
Some("Cow")
}
fn crate_name() -> Option<&'static str> {
Some("std")
}
fn module_path() -> Option<&'static str> {
Some("std::borrow")
}
}
impl GetTypeRegistration for Cow<'static, str> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
@ -1081,6 +1252,11 @@ impl Reflect for &'static Path {
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -1159,6 +1335,18 @@ impl Typed for &'static Path {
}
}
impl TypePath for &'static Path {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "&std::path::Path".to_owned())
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "&Path".to_owned())
}
}
impl GetTypeRegistration for &'static Path {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
@ -1178,6 +1366,11 @@ impl Reflect for Cow<'static, Path> {
std::any::type_name::<Self>()
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
@ -1264,6 +1457,11 @@ impl Typed for Cow<'static, Path> {
}
}
trait PathOnly: ToOwned {}
impl PathOnly for Path {}
impl_type_path!(::alloc::borrow::Cow<'a: 'static, T: PathOnly + ?Sized>);
impl_type_path!(::std::path::Path);
impl FromReflect for Cow<'static, Path> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
Some(reflect.as_any().downcast_ref::<Self>()?.clone())

View File

@ -448,6 +448,7 @@ mod struct_trait;
mod tuple;
mod tuple_struct;
mod type_info;
mod type_path;
mod type_registry;
mod type_uuid;
mod type_uuid_impl;
@ -496,12 +497,15 @@ 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::*;
pub use bevy_reflect_derive::*;
pub use erased_serde;
extern crate alloc;
#[doc(hidden)]
pub mod __macro_exports {
use crate::Uuid;
@ -543,8 +547,8 @@ mod tests {
ser::{to_string_pretty, PrettyConfig},
Deserializer,
};
use std::any::TypeId;
use std::fmt::{Debug, Formatter};
use std::{any::TypeId, marker::PhantomData};
use super::prelude::*;
use super::*;
@ -1118,6 +1122,98 @@ mod tests {
);
}
#[test]
fn reflect_type_path() {
#[derive(TypePath)]
struct Param;
#[derive(TypePath)]
struct Derive;
#[derive(TypePath)]
#[type_path = "my_alias"]
struct DerivePath;
#[derive(TypePath)]
#[type_path = "my_alias"]
#[type_name = "MyDerivePathName"]
struct DerivePathName;
#[derive(TypePath)]
struct DeriveG<T>(PhantomData<T>);
#[derive(TypePath)]
#[type_path = "my_alias"]
struct DerivePathG<T, const N: usize>(PhantomData<T>);
#[derive(TypePath)]
#[type_path = "my_alias"]
#[type_name = "MyDerivePathNameG"]
struct DerivePathNameG<T>(PhantomData<T>);
struct Macro;
impl_type_path!((in my_alias) Macro);
struct MacroName;
impl_type_path!((in my_alias as MyMacroName) MacroName);
struct MacroG<T, const N: usize>(PhantomData<T>);
impl_type_path!((in my_alias) MacroG<T, const N: usize>);
struct MacroNameG<T>(PhantomData<T>);
impl_type_path!((in my_alias as MyMacroNameG) MacroNameG<T>);
assert_eq!(Derive::type_path(), "bevy_reflect::tests::Derive");
assert_eq!(DerivePath::type_path(), "my_alias::DerivePath");
assert_eq!(DerivePathName::type_path(), "my_alias::MyDerivePathName");
assert_eq!(
DeriveG::<Param>::type_path(),
"bevy_reflect::tests::DeriveG<bevy_reflect::tests::Param>"
);
assert_eq!(
DerivePathG::<Param, 10>::type_path(),
"my_alias::DerivePathG<bevy_reflect::tests::Param, 10>"
);
assert_eq!(
DerivePathNameG::<Param>::type_path(),
"my_alias::MyDerivePathNameG<bevy_reflect::tests::Param>"
);
assert_eq!(Macro::type_path(), "my_alias::Macro");
assert_eq!(MacroName::type_path(), "my_alias::MyMacroName");
assert_eq!(
MacroG::<Param, 10>::type_path(),
"my_alias::MacroG<bevy_reflect::tests::Param, 10>"
);
assert_eq!(
MacroNameG::<Param>::type_path(),
"my_alias::MyMacroNameG<bevy_reflect::tests::Param>"
);
assert_eq!(Derive::short_type_path(), "Derive");
assert_eq!(DerivePath::short_type_path(), "DerivePath");
assert_eq!(DerivePathName::short_type_path(), "MyDerivePathName");
assert_eq!(DeriveG::<Param>::short_type_path(), "DeriveG<Param>");
assert_eq!(
DerivePathG::<Param, 10>::short_type_path(),
"DerivePathG<Param, 10>"
);
assert_eq!(
DerivePathNameG::<Param>::short_type_path(),
"MyDerivePathNameG<Param>"
);
assert_eq!(Macro::short_type_path(), "Macro");
assert_eq!(MacroName::short_type_path(), "MyMacroName");
assert_eq!(MacroG::<Param, 10>::short_type_path(), "MacroG<Param, 10>");
assert_eq!(
MacroNameG::<Param>::short_type_path(),
"MyMacroNameG<Param>"
);
}
#[test]
fn reflect_type_info() {
// TypeInfo
@ -1171,7 +1267,7 @@ mod tests {
// Struct (generic)
#[derive(Reflect)]
struct MyGenericStruct<T: Reflect> {
struct MyGenericStruct<T: Reflect + TypePath> {
foo: T,
bar: usize,
}
@ -1420,7 +1516,7 @@ mod tests {
struct SomePrimitive;
impl_reflect_value!(
/// Some primitive for which we have attributed custom documentation.
SomePrimitive
(in bevy_reflect::tests) SomePrimitive
);
let info = <SomePrimitive as Typed>::type_info();

View File

@ -2,8 +2,13 @@ use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use bevy_reflect_derive::impl_type_path;
use crate::utility::reflect_hasher;
use crate::{FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo};
use crate::{
self as bevy_reflect, DynamicTypePath, FromReflect, Reflect, ReflectMut, ReflectOwned,
ReflectRef, TypeInfo,
};
/// A trait used to power [list-like] operations via [reflection].
///
@ -271,6 +276,11 @@ impl Reflect for DynamicList {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -352,6 +362,8 @@ impl Reflect for DynamicList {
}
}
impl_type_path!((in bevy_reflect) DynamicList);
impl Debug for DynamicList {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.debug(f)

View File

@ -2,9 +2,12 @@ use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::hash::Hash;
use bevy_reflect_derive::impl_type_path;
use bevy_utils::{Entry, HashMap};
use crate::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo};
use crate::{
self as bevy_reflect, DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
};
/// A trait used to power [map-like] operations via [reflection].
///
@ -307,6 +310,11 @@ impl Reflect for DynamicMap {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -375,6 +383,8 @@ impl Reflect for DynamicMap {
}
}
impl_type_path!((in bevy_reflect) DynamicMap);
impl Debug for DynamicMap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.debug(f)

View File

@ -1,7 +1,7 @@
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, DynamicTypePath, Enum, List, Map, Struct, Tuple, TupleStruct,
TypeInfo, Typed, ValueInfo,
};
use std::{
any::{self, Any, TypeId},
@ -93,6 +93,14 @@ pub trait Reflect: Any + Send + Sync {
/// [`TypeRegistry::get_type_info`]: crate::TypeRegistry::get_type_info
fn get_represented_type_info(&self) -> Option<&'static TypeInfo>;
/// Returns the [`TypePath`] implementation for the underlying type.
///
/// Methods on [`DynamicTypePath`] suffer the same performance concerns as [`get_represented_type_info`].
///
/// [`TypePath`]: crate::TypePath
/// [`get_represented_type_info`]: Reflect::get_represented_type_info
fn get_type_path(&self) -> &dyn DynamicTypePath;
/// Returns the value as a [`Box<dyn Any>`][std::any::Any].
fn into_any(self: Box<Self>) -> Box<dyn Any>;

View File

@ -1,4 +1,8 @@
use crate::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo};
use crate::{
self as bevy_reflect, DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned,
ReflectRef, TypeInfo,
};
use bevy_reflect_derive::impl_type_path;
use bevy_utils::{Entry, HashMap};
use std::fmt::{Debug, Formatter};
use std::{
@ -401,6 +405,11 @@ impl Reflect for DynamicStruct {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -485,6 +494,8 @@ impl Reflect for DynamicStruct {
}
}
impl_type_path!((in bevy_reflect) DynamicStruct);
impl Debug for DynamicStruct {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.debug(f)

View File

@ -1,5 +1,8 @@
use bevy_reflect_derive::impl_type_path;
use crate::{
FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
self as bevy_reflect, utility::GenericTypePathCell, DynamicTypePath, FromReflect,
GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath,
TypeRegistration, Typed, UnnamedField,
};
use std::any::{Any, TypeId};
@ -318,6 +321,11 @@ impl Reflect for DynamicTuple {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -393,6 +401,8 @@ impl Reflect for DynamicTuple {
}
}
impl_type_path!((in bevy_reflect) DynamicTuple);
/// Applies the elements of `b` to the corresponding elements of `a`.
///
/// # Panics
@ -467,7 +477,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 {
@ -519,7 +529,7 @@ macro_rules! impl_reflect_tuple {
}
}
impl<$($name: Reflect),*> Reflect for ($($name,)*) {
impl<$($name: Reflect + TypePath),*> Reflect for ($($name,)*) {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
@ -528,6 +538,11 @@ macro_rules! impl_reflect_tuple {
Some(<Self as Typed>::type_info())
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
@ -582,7 +597,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::<Self, _>(|| {
@ -595,13 +610,30 @@ macro_rules! impl_reflect_tuple {
}
}
impl<$($name: Reflect + Typed),*> GetTypeRegistration for ($($name,)*) {
impl <$($name: Reflect + TypePath),*> TypePath for ($($name,)*) {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
"(".to_owned() $(+ <$name as TypePath>::type_path())* + ")"
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
"(".to_owned() $(+ <$name as TypePath>::short_type_path())* + ")"
})
}
}
impl<$($name: Reflect + TypePath),*> GetTypeRegistration for ($($name,)*) {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<($($name,)*)>()
}
}
impl<$($name: FromReflect),*> FromReflect for ($($name,)*)
impl<$($name: FromReflect + TypePath),*> FromReflect for ($($name,)*)
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() {

View File

@ -1,4 +1,9 @@
use crate::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, UnnamedField};
use bevy_reflect_derive::impl_type_path;
use crate::{
self as bevy_reflect, DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
UnnamedField,
};
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::slice::Iter;
@ -303,6 +308,11 @@ impl Reflect for DynamicTupleStruct {
self.represented_type
}
#[inline]
fn get_type_path(&self) -> &dyn DynamicTypePath {
self
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
@ -386,6 +396,8 @@ impl Reflect for DynamicTupleStruct {
}
}
impl_type_path!((in bevy_reflect) DynamicTupleStruct);
impl Debug for DynamicTupleStruct {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.debug(f)

View File

@ -24,7 +24,7 @@ use std::fmt::Debug;
///
/// ```
/// # use std::any::Any;
/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, ValueInfo};
/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, ValueInfo};
/// # use bevy_reflect::utility::NonGenericTypeInfoCell;
/// use bevy_reflect::Typed;
///
@ -51,6 +51,7 @@ use std::fmt::Debug;
/// # impl Reflect for MyStruct {
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }

View File

@ -0,0 +1,167 @@
/// A static accessor to type paths and names.
///
/// The engine uses this trait over [`std::any::type_name`] for stability and flexibility.
///
/// This trait is automatically implemented by the `#[derive(Reflect)]` macro
/// and allows type path information to be processed without an instance of that type.
///
/// Implementors may have difficulty in generating references with static
/// lifetimes. Luckily, this crate comes with some [utility] structs, to make generating these
/// statics much simpler.
///
/// # Stability
///
/// Certain parts of the engine, e.g. [(de)serialization], rely on type paths as identifiers
/// for matching dynamic values to concrete types.
///
/// Using [`std::any::type_name`], a scene containing `my_crate::foo::MyComponent` would break,
/// failing to deserialize if the component was moved from the `foo` module to the `bar` module,
/// becoming `my_crate::bar::MyComponent`.
/// This trait, through attributes when deriving itself or [`Reflect`], can ensure breaking changes are avoidable.
///
/// The only external factor we rely on for stability when deriving is the [`module_path!`] macro,
/// only if the derive does not provide a `#[type_path = "..."]` attribute.
///
/// # Anonymity
///
/// Some methods on this trait return `Option<&'static str>` over `&'static str`
/// because not all types define all parts of a type path, for example the array type `[T; N]`.
///
/// Such types are 'anonymous' in that they have only a defined [`type_path`] and [`short_type_path`]
/// and the methods [`crate_name`], [`module_path`] and [`type_ident`] all return `None`.
///
/// Primitives are treated like anonymous types, except they also have a defined [`type_ident`].
///
/// # Example
///
/// ```rust
/// use bevy_reflect::TypePath;
///
/// // This type path will not change with compiler versions or recompiles,
/// // although it will not be the same if the definition is moved.
/// #[derive(TypePath)]
/// struct NonStableTypePath;
///
/// // This type path will never change, even if the definition is moved.
/// #[derive(TypePath)]
/// #[type_path = "my_crate::foo"]
/// struct StableTypePath;
///
/// // Type paths can have any number of path segments.
/// #[derive(TypePath)]
/// #[type_path = "my_crate::foo::bar::baz"]
/// struct DeeplyNestedStableTypePath;
///
/// // Including just a crate name!
/// #[derive(TypePath)]
/// #[type_path = "my_crate"]
/// struct ShallowStableTypePath;
///
/// // We can also rename the identifier/name of types.
/// #[derive(TypePath)]
/// #[type_path = "my_crate::foo"]
/// #[type_name = "RenamedStableTypePath"]
/// struct NamedStableTypePath;
///
/// // Generics are also supported.
/// #[derive(TypePath)]
/// #[type_path = "my_crate::foo"]
/// struct StableGenericTypePath<T, const N: usize>([T; N]);
/// ```
///
/// [utility]: crate::utility
/// [(de)serialization]: crate::serde::UntypedReflectDeserializer
/// [`Reflect`]: crate::Reflect
/// [`type_path`]: TypePath::type_path
/// [`short_type_path`]: TypePath::short_type_path
/// [`crate_name`]: TypePath::crate_name
/// [`module_path`]: TypePath::module_path
/// [`type_ident`]: TypePath::type_ident
pub trait TypePath: 'static {
/// Returns the fully qualified path of the underlying type.
///
/// Generic parameter types are also fully expanded.
///
/// For `Option<PhantomData>`, this is `"core::option::Option<core::marker::PhantomData>"`.
fn type_path() -> &'static str;
/// Returns a short, pretty-print enabled path to the type.
///
/// Generic parameter types are also shortened.
///
/// For `Option<PhantomData>`, this is `"Option<PhantomData>"`.
fn short_type_path() -> &'static str;
/// Returns the name of the type, or [`None`] if it is [anonymous].
///
/// Primitive types will return [`Some`].
///
/// For `Option<PhantomData>`, this is `"Option"`.
///
/// [anonymous]: TypePath#anonymity
fn type_ident() -> Option<&'static str> {
None
}
/// Returns the name of the crate the type is in, or [`None`] if it is [anonymous].
///
/// For `Option<PhantomData>`, this is `"core"`.
///
/// [anonymous]: TypePath#anonymity
fn crate_name() -> Option<&'static str> {
None
}
/// Returns the path to the moudle the type is in, or [`None`] if it is [anonymous].
///
/// For `Option<PhantomData>`, this is `"core::option"`.
///
/// [anonymous]: TypePath#anonymity
fn module_path() -> Option<&'static str> {
None
}
}
/// Dynamic dispatch for [`TypePath`].
///
/// Retrieved using [`Reflect::get_type_path`].
///
/// [`Reflect::get_type_path`]: crate::Reflect::get_type_path
pub trait DynamicTypePath {
/// See [`TypePath::type_path`].
fn reflect_type_path(&self) -> &str;
/// See [`TypePath::short_type_path`].
fn reflect_short_type_path(&self) -> &str;
/// See [`TypePath::type_ident`].
fn reflect_type_ident(&self) -> Option<&str>;
/// See [`TypePath::crate_name`].
fn reflect_crate_name(&self) -> Option<&str>;
/// See [`TypePath::module_path`].
fn reflect_module_path(&self) -> Option<&str>;
}
impl<T: TypePath> DynamicTypePath for T {
fn reflect_type_path(&self) -> &str {
Self::type_path()
}
fn reflect_short_type_path(&self) -> &str {
Self::short_type_path()
}
fn reflect_type_ident(&self) -> Option<&str> {
Self::type_ident()
}
fn reflect_crate_name(&self) -> Option<&str> {
Self::crate_name()
}
fn reflect_module_path(&self) -> Option<&str> {
Self::module_path()
}
}

View File

@ -9,132 +9,220 @@ use std::{
hash::BuildHasher,
};
/// A type that can be stored in a ([`Non`])[`GenericTypeCell`].
///
/// [`Non`]: NonGenericTypeCell
pub trait TypedProperty: sealed::Sealed {
type Stored: 'static;
}
/// Used to store a [`String`] in a [`GenericTypePathCell`] as part of a [`TypePath`] implementation.
///
/// [`TypePath`]: crate::TypePath
pub struct TypePathComponent;
mod sealed {
use super::{TypeInfo, TypePathComponent, TypedProperty};
pub trait Sealed {}
impl Sealed for TypeInfo {}
impl Sealed for TypePathComponent {}
impl TypedProperty for TypeInfo {
type Stored = Self;
}
impl TypedProperty for TypePathComponent {
type Stored = String;
}
}
/// 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,
/// then use [`GenericTypeInfoCell`] instead. Otherwise, it will not take into account all
/// then use [`GenericTypeCell`] instead. Otherwise, it will not take into account all
/// monomorphizations of your type.
///
/// Non-generic [`TypePath`]s should be trivially generated with string literals and [`concat!`].
///
/// ## Example
///
/// ```
/// # use std::any::Any;
/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo};
/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo};
/// use bevy_reflect::utility::NonGenericTypeInfoCell;
///
/// struct Foo {
/// bar: i32
/// bar: i32
/// }
///
/// impl Typed for Foo {
/// fn type_info() -> &'static TypeInfo {
/// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
/// CELL.get_or_set(|| {
/// let fields = [NamedField::new::<i32>("bar")];
/// let info = StructInfo::new::<Self>("Foo", &fields);
/// TypeInfo::Struct(info)
/// })
/// }
/// fn type_info() -> &'static TypeInfo {
/// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
/// CELL.get_or_set(|| {
/// let fields = [NamedField::new::<i32>("bar")];
/// let info = StructInfo::new::<Self>("Foo", &fields);
/// TypeInfo::Struct(info)
/// })
/// }
/// }
/// #
/// # impl Reflect for Foo {
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
/// # fn as_reflect(&self) -> &dyn Reflect { todo!() }
/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
/// # fn apply(&mut self, value: &dyn Reflect) { todo!() }
/// # fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
/// # fn as_reflect(&self) -> &dyn Reflect { todo!() }
/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
/// # fn apply(&mut self, value: &dyn Reflect) { todo!() }
/// # fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # }
/// ```
pub struct NonGenericTypeInfoCell(OnceBox<TypeInfo>);
///
/// [`TypePath`]: crate::TypePath
pub struct NonGenericTypeCell<T: TypedProperty>(OnceBox<T::Stored>);
impl NonGenericTypeInfoCell {
/// Initialize a [`NonGenericTypeInfoCell`] for non-generic types.
/// See [`NonGenericTypeCell`].
pub type NonGenericTypeInfoCell = NonGenericTypeCell<TypeInfo>;
impl<T: TypedProperty> NonGenericTypeCell<T> {
/// Initialize a [`NonGenericTypeCell`] for non-generic types.
pub const fn new() -> Self {
Self(OnceBox::new())
}
/// Returns a reference to the [`TypeInfo`] stored in the cell.
/// Returns a reference to the [`TypedProperty`] 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<F>(&self, f: F) -> &TypeInfo
/// If there is no entry found, a new one will be generated from the given function.
pub fn get_or_set<F>(&self, f: F) -> &T::Stored
where
F: FnOnce() -> TypeInfo,
F: FnOnce() -> T::Stored,
{
self.0.get_or_init(|| Box::new(f()))
}
}
/// A container for [`TypeInfo`] over generic types, allowing instances to be stored statically.
/// A container for [`TypedProperty`] 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 [`NonGenericTypeInfoCell`] instead as it should be much more performant.
/// then use [`NonGenericTypeCell`] instead as it should be much more performant.
///
/// ## Example
/// `#[derive(TypePath)]` and [`impl_type_path`] should always be used over [`GenericTypePathCell`]
/// where possible.
///
/// ## Examples
///
/// Implementing [`TypeInfo`] with generics.
///
/// ```
/// # use std::any::Any;
/// # use bevy_reflect::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField};
/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField};
/// use bevy_reflect::utility::GenericTypeInfoCell;
///
/// struct Foo<T: Reflect>(T);
/// struct Foo<T>(T);
///
/// impl<T: Reflect> Typed for Foo<T> {
/// fn type_info() -> &'static TypeInfo {
/// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
/// CELL.get_or_insert::<Self, _>(|| {
/// let fields = [UnnamedField::new::<T>(0)];
/// let info = TupleStructInfo::new::<Self>("Foo", &fields);
/// TypeInfo::TupleStruct(info)
/// })
/// }
/// fn type_info() -> &'static TypeInfo {
/// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
/// CELL.get_or_insert::<Self, _>(|| {
/// let fields = [UnnamedField::new::<T>(0)];
/// let info = TupleStructInfo::new::<Self>("Foo", &fields);
/// TypeInfo::TupleStruct(info)
/// })
/// }
/// }
/// #
/// # impl<T: Reflect> Reflect for Foo<T> {
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
/// # fn as_reflect(&self) -> &dyn Reflect { todo!() }
/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
/// # fn apply(&mut self, value: &dyn Reflect) { todo!() }
/// # fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
/// # fn as_reflect(&self) -> &dyn Reflect { todo!() }
/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
/// # fn apply(&mut self, value: &dyn Reflect) { todo!() }
/// # fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # }
/// ```
pub struct GenericTypeInfoCell(OnceBox<RwLock<HashMap<TypeId, &'static TypeInfo>>>);
///
/// Implementing [`TypePath`] with generics.
///
/// ```
/// # use std::any::Any;
/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath};
/// use bevy_reflect::utility::GenericTypePathCell;
///
/// struct Foo<T>(T);
///
/// impl<T: Reflect + TypePath> TypePath for Foo<T> {
/// fn type_path() -> &'static str {
/// static CELL: GenericTypePathCell = GenericTypePathCell::new();
/// CELL.get_or_insert::<Self, _>(|| format!("my_crate::foo::Foo<{}>", T::type_path()))
/// }
///
/// fn short_type_path() -> &'static str {
/// static CELL: GenericTypePathCell = GenericTypePathCell::new();
/// CELL.get_or_insert::<Self, _>(|| format!("Foo<{}>", T::short_type_path()))
/// }
/// }
/// #
/// # impl<T: Reflect> Reflect for Foo<T> {
/// # fn type_name(&self) -> &str { todo!() }
/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() }
/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
/// # fn as_any(&self) -> &dyn Any { todo!() }
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
/// # fn as_reflect(&self) -> &dyn Reflect { todo!() }
/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
/// # fn apply(&mut self, value: &dyn Reflect) { todo!() }
/// # fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
/// # fn clone_value(&self) -> Box<dyn Reflect> { todo!() }
/// # }
/// ```
/// [`impl_type_path`]: crate::impl_type_path
/// [`TypePath`]: crate::TypePath
pub struct GenericTypeCell<T: TypedProperty>(OnceBox<RwLock<HashMap<TypeId, &'static T::Stored>>>);
impl GenericTypeInfoCell {
/// Initialize a [`GenericTypeInfoCell`] for generic types.
/// See [`GenericTypeCell`].
pub type GenericTypeInfoCell = GenericTypeCell<TypeInfo>;
/// See [`GenericTypeCell`].
pub type GenericTypePathCell = GenericTypeCell<TypePathComponent>;
impl<T: TypedProperty> GenericTypeCell<T> {
/// Initialize a [`GenericTypeCell`] for generic types.
pub const fn new() -> Self {
Self(OnceBox::new())
}
/// Returns a reference to the [`TypeInfo`] stored in the cell.
/// Returns a reference to the [`TypedProperty`] 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<T, F>(&self, f: F) -> &TypeInfo
/// This method will then return the correct [`TypedProperty`] reference for the given type `T`.
/// If there is no entry found, a new one will be generated from the given function.
pub fn get_or_insert<G, F>(&self, f: F) -> &T::Stored
where
T: Any + ?Sized,
F: FnOnce() -> TypeInfo,
G: Any + ?Sized,
F: FnOnce() -> T::Stored,
{
let type_id = TypeId::of::<T>();
let type_id = TypeId::of::<G>();
// let mapping = self.0.get_or_init(|| Box::new(RwLock::default()));
let mapping = self.0.get_or_init(Box::default);
if let Some(info) = mapping.read().get(&type_id) {

View File

@ -1,4 +1,4 @@
use bevy_reflect::Reflect;
use bevy_reflect::{Reflect, TypePath};
#[derive(Reflect)]
struct Foo<T> {
@ -6,10 +6,11 @@ struct Foo<T> {
}
// Type that doesn't implement Reflect
#[derive(TypePath)]
struct NoReflect(f32);
fn main() {
let mut foo: Box<dyn Reflect> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
// foo doesn't implement Reflect because NoReflect doesn't implement Reflect
foo.get_field::<NoReflect>("a").unwrap();
}
}

View File

@ -1,13 +1,13 @@
error[E0599]: no method named `get_field` found for struct `Box<(dyn Reflect + 'static)>` in the current scope
--> tests/reflect_derive/generics.fail.rs:14:9
--> tests/reflect_derive/generics.fail.rs:15:9
|
14 | foo.get_field::<NoReflect>("a").unwrap();
15 | foo.get_field::<NoReflect>("a").unwrap();
| ^^^^^^^^^ method not found in `Box<dyn Reflect>`
error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied
--> tests/reflect_derive/generics.fail.rs:12:37
--> tests/reflect_derive/generics.fail.rs:13:37
|
12 | let mut foo: Box<dyn Reflect> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
13 | let mut foo: Box<dyn Reflect> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect`
|
= help: the following other types implement trait `Reflect`:

View File

@ -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, TypePath, Clone)]
#[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"]
pub struct Mesh {
primitive_topology: PrimitiveTopology,

View File

@ -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)]
@ -24,7 +24,7 @@ impl MapEntities for SkinnedMesh {
}
}
#[derive(Debug, TypeUuid)]
#[derive(Debug, TypeUuid, TypePath)]
#[uuid = "b9f155a9-54ec-4026-988f-e0a03e99a76f"]
pub struct SkinnedMeshInverseBindposes(Box<[Mat4]>);

View File

@ -1,7 +1,7 @@
use super::ShaderDefVal;
use crate::define_atomic_id;
use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset};
use bevy_reflect::TypeUuid;
use bevy_reflect::{TypePath, TypeUuid};
use bevy_utils::{tracing::error, BoxedFuture, HashMap};
#[cfg(feature = "shader_format_glsl")]
use naga::back::wgsl::WriterFlags;
@ -31,7 +31,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,

View File

@ -69,7 +69,7 @@ bitflags::bitflags! {
const VISIBLE_IN_HIERARCHY = 1 << 1;
}
}
bevy_reflect::impl_reflect_value!(ComputedVisibilityFlags);
bevy_reflect::impl_reflect_value!((in bevy_render::view) ComputedVisibilityFlags);
bevy_reflect::impl_from_reflect_value!(ComputedVisibilityFlags);
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering

View File

@ -8,7 +8,7 @@ use bevy_ecs::{
reflect::{ReflectComponent, ReflectMapEntities},
world::World,
};
use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid};
use bevy_reflect::{Reflect, TypePath, TypeRegistryArc, TypeUuid};
use bevy_utils::HashMap;
#[cfg(feature = "serialize")]
@ -26,7 +26,7 @@ use serde::Serialize;
/// * adding the [`Handle<DynamicScene>`](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 resources: Vec<Box<dyn Reflect>>,

View File

@ -4,7 +4,7 @@ use bevy_ecs::{
reflect::{ReflectComponent, ReflectMapEntities, ReflectResource},
world::World,
};
use bevy_reflect::TypeUuid;
use bevy_reflect::{TypePath, TypeUuid};
use crate::{DynamicScene, InstanceInfo, SceneSpawnError};
@ -14,7 +14,7 @@ use crate::{DynamicScene, InstanceInfo, SceneSpawnError};
/// * adding the [`Handle<Scene>`](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,

View File

@ -14,7 +14,7 @@ use bevy_ecs::{
},
};
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,7 @@ 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 {
/// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader
/// will be used.
fn vertex_shader() -> ShaderRef {

View File

@ -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, Clone)]
#[derive(Debug, TypeUuid, TypePath, Clone)]
#[uuid = "97059ac6-c9ba-4da9-95b6-bed82c3ce198"]
pub struct Font {
pub font: FontArc,

View File

@ -2,6 +2,7 @@ use crate::{error::TextError, Font, FontAtlas};
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<FontSizeKey, Vec<FontAtlas>>,

View File

@ -2,7 +2,7 @@
use bevy::gltf::GltfPlugin;
use bevy::prelude::*;
use bevy::reflect::TypeUuid;
use bevy::reflect::{TypePath, TypeUuid};
use bevy::render::mesh::{MeshVertexAttribute, MeshVertexBufferLayout};
use bevy::render::render_resource::*;
use bevy::sprite::{
@ -55,7 +55,7 @@ fn setup(
/// This custom material uses barycentric coordinates from
/// `ATTRIBUTE_BARYCENTRIC` to shade a white border around each triangle. The
/// thickness of the border is animated using the global time shader uniform.
#[derive(AsBindGroup, TypeUuid, Debug, Clone)]
#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)]
#[uuid = "50ffce9e-1582-42e9-87cb-2233724426c0"]
struct CustomMaterial {}

View File

@ -3,7 +3,7 @@
use bevy::{
pbr::{MaterialPipeline, MaterialPipelineKey},
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::{
mesh::{MeshVertexBufferLayout, PrimitiveTopology},
render_resource::{
@ -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)]

View File

@ -5,7 +5,7 @@ use bevy::{
math::vec2,
pbr::CascadeShadowConfigBuilder,
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::{
render_resource::{
AsBindGroup, Extent3d, SamplerDescriptor, ShaderRef, TextureDimension, TextureFormat,
@ -689,7 +689,7 @@ impl Material for ColorGradientMaterial {
}
}
#[derive(AsBindGroup, Debug, Clone, TypeUuid)]
#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)]
#[uuid = "117f64fe-6844-1822-8926-e3ed372291c8"]
pub struct ColorGradientMaterial {}

View File

@ -3,12 +3,12 @@
use bevy::{
asset::{AssetLoader, LoadContext, LoadedAsset},
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
utils::BoxedFuture,
};
use serde::Deserialize;
#[derive(Debug, Deserialize, TypeUuid)]
#[derive(Debug, Deserialize, TypeUuid, TypePath)]
#[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"]
pub struct CustomAsset {
pub value: i32,

View File

@ -3,14 +3,14 @@ use bevy::audio::AddAudioSource;
use bevy::audio::AudioPlugin;
use bevy::audio::Source;
use bevy::prelude::*;
use bevy::reflect::TypeUuid;
use bevy::reflect::{TypePath, TypeUuid};
use bevy::utils::Duration;
// This struct usually contains the data for the audio being played.
// This is where data read from an audio file would be stored, for example.
// Implementing `TypeUuid` will automatically implement `Asset`.
// This allows the type to be registered as an asset.
#[derive(TypeUuid)]
#[derive(TypePath, TypeUuid)]
#[uuid = "c2090c23-78fd-44f1-8508-c89b1f3cec29"]
struct SineAudio {
frequency: f32,

View File

@ -1,7 +1,11 @@
//! A shader that uses dynamic data like the time since startup.
//! The time data is in the globals binding which is part of the `mesh_view_bindings` shader import.
use bevy::{prelude::*, reflect::TypeUuid, render::render_resource::*};
use bevy::{
prelude::*,
reflect::{TypePath, TypeUuid},
render::render_resource::*,
};
fn main() {
App::new()
@ -31,7 +35,7 @@ fn setup(
});
}
#[derive(AsBindGroup, TypeUuid, Debug, Clone)]
#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)]
#[uuid = "a3d71c04-d054-4946-80f8-ba6cfbc90cad"]
struct CustomMaterial {}

View File

@ -1,7 +1,7 @@
use bevy::{
asset::LoadState,
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef},
};
@ -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")]

View File

@ -3,7 +3,7 @@
use bevy::{
pbr::{MaterialPipeline, MaterialPipelineKey},
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::{
mesh::{MeshVertexAttribute, MeshVertexBufferLayout},
render_resource::{
@ -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)]

View File

@ -3,7 +3,7 @@
use bevy::{
pbr::{MaterialPipeline, MaterialPipelineKey},
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::{
mesh::MeshVertexBufferLayout,
render_resource::{
@ -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 {

View File

@ -2,7 +2,7 @@
use bevy::{
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef},
};
@ -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)]

View File

@ -3,7 +3,7 @@
use bevy::{
pbr::{MaterialPipeline, MaterialPipelineKey},
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::{
mesh::MeshVertexBufferLayout,
render_resource::{
@ -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)]

View File

@ -2,7 +2,7 @@
use bevy::{
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef},
};
@ -66,7 +66,7 @@ fn rotate_camera(mut camera: Query<&mut Transform, With<MainCamera>>, 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)]

View File

@ -6,7 +6,7 @@ use bevy::{
core_pipeline::prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass},
pbr::{NotShadowCaster, PbrPlugin},
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef, ShaderType},
};
@ -155,7 +155,7 @@ fn setup(
}
// This is the struct that will be passed to your shader
#[derive(AsBindGroup, TypeUuid, Debug, Clone)]
#[derive(AsBindGroup, TypePath, TypeUuid, Debug, Clone)]
#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"]
pub struct CustomMaterial {
#[uniform(0)]
@ -204,7 +204,7 @@ struct ShowPrepassSettings {
}
// This shader simply loads the prepass texture and outputs it directly
#[derive(AsBindGroup, TypeUuid, Debug, Clone)]
#[derive(AsBindGroup, TypePath, TypeUuid, Debug, Clone)]
#[uuid = "0af99895-b96e-4451-bc12-c6b1c1c52750"]
pub struct PrepassOutputMaterial {
#[uniform(0)]

View File

@ -3,7 +3,7 @@
use bevy::{
prelude::*,
reflect::TypeUuid,
reflect::{TypePath, TypeUuid},
render::{
render_asset::RenderAssets,
render_resource::{AsBindGroupError, PreparedBindGroup, *},
@ -85,7 +85,7 @@ fn setup(
});
}
#[derive(Debug, Clone, TypeUuid)]
#[derive(Debug, Clone, TypePath, TypeUuid)]
#[uuid = "8dd2b424-45a2-4a53-ac29-7ce356b2d5fe"]
struct BindlessMaterial {
textures: Vec<Handle<Image>>,