bevy/crates/bevy_reflect/src/fields.rs
radiish 6ab8767d3b
reflect: implement the unique reflect rfc (#7207)
# Objective

- Implements the [Unique Reflect
RFC](https://github.com/nicopap/rfcs/blob/bevy-reflect-api/rfcs/56-better-reflect.md).

## Solution

- Implements the RFC.
- This implementation differs in some ways from the RFC:
- In the RFC, it was suggested `Reflect: Any` but `PartialReflect:
?Any`. During initial implementation I tried this, but we assume the
`PartialReflect: 'static` in a lot of places and the changes required
crept out of the scope of this PR.
- `PartialReflect::try_into_reflect` originally returned `Option<Box<dyn
Reflect>>` but i changed this to `Result<Box<dyn Reflect>, Box<dyn
PartialReflect>>` since the method takes by value and otherwise there
would be no way to recover the type. `as_full` and `as_full_mut` both
still return `Option<&(mut) dyn Reflect>`.

---

## Changelog

- Added `PartialReflect`.
- `Reflect` is now a subtrait of `PartialReflect`.
- Moved most methods on `Reflect` to the new `PartialReflect`.
- Added `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect}`.
- Added `PartialReflect::{try_as_reflect, try_as_reflect_mut,
try_into_reflect}`.
- Added `<dyn PartialReflect>::{try_downcast_ref, try_downcast_mut,
try_downcast, try_take}` supplementing the methods on `dyn Reflect`.

## Migration Guide

- Most instances of `dyn Reflect` should be changed to `dyn
PartialReflect` which is less restrictive, however trait bounds should
generally stay as `T: Reflect`.
- The new `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect, try_as_reflect, try_as_reflect_mut,
try_into_reflect}` methods as well as `Reflect::{as_reflect,
as_reflect_mut, into_reflect}` will need to be implemented for manual
implementors of `Reflect`.

## Future Work

- This PR is designed to be followed up by another "Unique Reflect Phase
2" that addresses the following points:
- Investigate making serialization revolve around `Reflect` instead of
`PartialReflect`.
- [Remove the `try_*` methods on `dyn PartialReflect` since they are
stop
gaps](https://github.com/bevyengine/bevy/pull/7207#discussion_r1083476050).
- Investigate usages like `ReflectComponent`. In the places they
currently use `PartialReflect`, should they be changed to use `Reflect`?
- Merging this opens the door to lots of reflection features we haven't
been able to implement.
- We could re-add [the `Reflectable`
trait](8e3488c880/crates/bevy_reflect/src/reflect.rs (L337-L342))
and make `FromReflect` a requirement to improve [`FromReflect`
ergonomics](https://github.com/bevyengine/rfcs/pull/59). This is
currently not possible because dynamic types cannot sensibly be
`FromReflect`.
  - Since this is an alternative to #5772, #5781 would be made cleaner.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2024-08-12 17:01:41 +00:00

184 lines
5.3 KiB
Rust

use crate::attributes::{impl_custom_attribute_methods, CustomAttributes};
use crate::{MaybeTyped, PartialReflect, TypeInfo, TypePath, TypePathTable};
use std::any::{Any, TypeId};
use std::sync::Arc;
/// The named field of a reflected struct.
#[derive(Clone, Debug)]
pub struct NamedField {
name: &'static str,
type_info: fn() -> Option<&'static TypeInfo>,
type_path: TypePathTable,
type_id: TypeId,
custom_attributes: Arc<CustomAttributes>,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
impl NamedField {
/// Create a new [`NamedField`].
pub fn new<T: PartialReflect + MaybeTyped + TypePath>(name: &'static str) -> Self {
Self {
name,
type_info: T::maybe_type_info,
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
custom_attributes: Arc::new(CustomAttributes::default()),
#[cfg(feature = "documentation")]
docs: None,
}
}
/// Sets the docstring for this field.
#[cfg(feature = "documentation")]
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
Self { docs, ..self }
}
/// Sets the custom attributes for this field.
pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
Self {
custom_attributes: Arc::new(custom_attributes),
..self
}
}
/// The name of the field.
pub fn name(&self) -> &'static str {
self.name
}
/// The [`TypeInfo`] of the field.
///
///
/// Returns `None` if the field does not contain static type information,
/// such as for dynamic types.
pub fn type_info(&self) -> Option<&'static TypeInfo> {
(self.type_info)()
}
/// A representation of the type path of the field.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the field.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the field.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the field type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
/// The docstring of this field, if any.
#[cfg(feature = "documentation")]
pub fn docs(&self) -> Option<&'static str> {
self.docs
}
impl_custom_attribute_methods!(self.custom_attributes, "field");
}
/// The unnamed field of a reflected tuple or tuple struct.
#[derive(Clone, Debug)]
pub struct UnnamedField {
index: usize,
type_info: fn() -> Option<&'static TypeInfo>,
type_path: TypePathTable,
type_id: TypeId,
custom_attributes: Arc<CustomAttributes>,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
impl UnnamedField {
pub fn new<T: PartialReflect + MaybeTyped + TypePath>(index: usize) -> Self {
Self {
index,
type_info: T::maybe_type_info,
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
custom_attributes: Arc::new(CustomAttributes::default()),
#[cfg(feature = "documentation")]
docs: None,
}
}
/// Sets the docstring for this field.
#[cfg(feature = "documentation")]
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
Self { docs, ..self }
}
/// Sets the custom attributes for this field.
pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
Self {
custom_attributes: Arc::new(custom_attributes),
..self
}
}
/// Returns the index of the field.
pub fn index(&self) -> usize {
self.index
}
/// The [`TypeInfo`] of the field.
///
///
/// Returns `None` if the field does not contain static type information,
/// such as for dynamic types.
pub fn type_info(&self) -> Option<&'static TypeInfo> {
(self.type_info)()
}
/// A representation of the type path of the field.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the field.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the field.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the field type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
/// The docstring of this field, if any.
#[cfg(feature = "documentation")]
pub fn docs(&self) -> Option<&'static str> {
self.docs
}
impl_custom_attribute_methods!(self.custom_attributes, "field");
}