mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Finish the work on try_apply
(#12646)
# Objective Finish the `try_apply` implementation started in #6770 by @feyokorenhof. Supersedes and closes #6770. Closes #6182 ## Solution Add `try_apply` to `Reflect` and implement it in all the places that implement `Reflect`. --- ## Changelog Added `try_apply` to `Reflect`. --------- Co-authored-by: Feyo Korenhof <feyokorenhof@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
parent
2f87bb8c1f
commit
9c4ac7c297
21 changed files with 474 additions and 115 deletions
|
@ -21,7 +21,7 @@ pub(crate) struct EnumVariantConstructors {
|
|||
pub(crate) fn get_variant_constructors(
|
||||
reflect_enum: &ReflectEnum,
|
||||
ref_value: &Ident,
|
||||
can_panic: bool,
|
||||
return_apply_error: bool,
|
||||
) -> EnumVariantConstructors {
|
||||
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
|
||||
let variant_count = reflect_enum.variants().len();
|
||||
|
@ -50,21 +50,6 @@ pub(crate) fn get_variant_constructors(
|
|||
_ => quote! { #FQDefault::default() }
|
||||
}
|
||||
} else {
|
||||
let (resolve_error, resolve_missing) = if can_panic {
|
||||
let field_ref_str = match &field_ident {
|
||||
Member::Named(ident) => format!("the field `{ident}`"),
|
||||
Member::Unnamed(index) => format!("the field at index {}", index.index)
|
||||
};
|
||||
let ty = field.data.ty.to_token_stream();
|
||||
|
||||
let on_error = format!("{field_ref_str} should be of type `{ty}`");
|
||||
let on_missing = format!("{field_ref_str} is required but could not be found");
|
||||
|
||||
(quote!(.expect(#on_error)), quote!(.expect(#on_missing)))
|
||||
} else {
|
||||
(quote!(?), quote!(?))
|
||||
};
|
||||
|
||||
let field_accessor = match &field.data.ident {
|
||||
Some(ident) => {
|
||||
let name = ident.to_string();
|
||||
|
@ -74,6 +59,31 @@ pub(crate) fn get_variant_constructors(
|
|||
};
|
||||
reflect_index += 1;
|
||||
|
||||
let (resolve_error, resolve_missing) = if return_apply_error {
|
||||
let field_ref_str = match &field_ident {
|
||||
Member::Named(ident) => format!("{ident}"),
|
||||
Member::Unnamed(index) => format!(".{}", index.index)
|
||||
};
|
||||
let ty = field.data.ty.to_token_stream();
|
||||
|
||||
(
|
||||
quote!(.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
|
||||
// The unwrap won't panic. By this point the #field_accessor would have been invoked once and any failure to
|
||||
// access the given field handled by the `resolve_missing` code bellow.
|
||||
from_type: ::core::convert::Into::into(
|
||||
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#FQOption::unwrap(#field_accessor))
|
||||
),
|
||||
to_type: ::core::convert::Into::into(<#ty as #bevy_reflect_path::TypePath>::type_path())
|
||||
})?),
|
||||
quote!(.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
|
||||
variant_name: ::core::convert::Into::into(#name),
|
||||
field_name: ::core::convert::Into::into(#field_ref_str)
|
||||
})?)
|
||||
)
|
||||
} else {
|
||||
(quote!(?), quote!(?))
|
||||
};
|
||||
|
||||
match &field.attrs.default {
|
||||
DefaultBehavior::Func(path) => quote! {
|
||||
if let #FQOption::Some(field) = #field_accessor {
|
||||
|
|
|
@ -230,7 +230,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn apply(&mut self, #ref_value: &dyn #bevy_reflect_path::Reflect) {
|
||||
fn try_apply(&mut self, #ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
|
||||
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) {
|
||||
if #bevy_reflect_path::Enum::variant_name(self) == #bevy_reflect_path::Enum::variant_name(#ref_value) {
|
||||
// Same variant -> just update fields
|
||||
|
@ -238,12 +238,16 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
|
|||
#bevy_reflect_path::VariantType::Struct => {
|
||||
for field in #bevy_reflect_path::Enum::iter_fields(#ref_value) {
|
||||
let name = field.name().unwrap();
|
||||
#bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field.value()));
|
||||
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_mut(self, name) {
|
||||
#bevy_reflect_path::Reflect::try_apply(v, field.value())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
#bevy_reflect_path::VariantType::Tuple => {
|
||||
for (index, field) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Enum::iter_fields(#ref_value)) {
|
||||
#bevy_reflect_path::Enum::field_at_mut(self, index).map(|v| v.apply(field.value()));
|
||||
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_at_mut(self, index) {
|
||||
#bevy_reflect_path::Reflect::try_apply(v, field.value())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
@ -254,12 +258,25 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
|
|||
#(#variant_names => {
|
||||
*self = #variant_constructors
|
||||
})*
|
||||
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
|
||||
name => {
|
||||
return #FQResult::Err(
|
||||
#bevy_reflect_path::ApplyError::UnknownVariant {
|
||||
enum_name: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(self)),
|
||||
variant_name: ::core::convert::Into::into(name),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("`{}` is not an enum", #bevy_reflect_path::DynamicTypePath::reflect_type_path(#ref_value));
|
||||
return #FQResult::Err(
|
||||
#bevy_reflect_path::ApplyError::MismatchedKinds {
|
||||
from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value),
|
||||
to_kind: #bevy_reflect_path::ReflectKind::Enum,
|
||||
}
|
||||
);
|
||||
}
|
||||
#FQResult::Ok(())
|
||||
}
|
||||
|
||||
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
|
||||
|
|
|
@ -208,29 +208,37 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
|
||||
fn try_apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
|
||||
if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = #bevy_reflect_path::Reflect::reflect_ref(value) {
|
||||
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Struct::iter_fields(struct_value)) {
|
||||
let name = #bevy_reflect_path::Struct::name_at(struct_value, i).unwrap();
|
||||
#bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value));
|
||||
if let #FQOption::Some(v) = #bevy_reflect_path::Struct::field_mut(self, name) {
|
||||
#bevy_reflect_path::Reflect::try_apply(v, value)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply non-struct type to struct type.");
|
||||
return #FQResult::Err(
|
||||
#bevy_reflect_path::ApplyError::MismatchedKinds {
|
||||
from_kind: #bevy_reflect_path::Reflect::reflect_kind(value),
|
||||
to_kind: #bevy_reflect_path::ReflectKind::Struct
|
||||
}
|
||||
);
|
||||
}
|
||||
#FQResult::Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
|
||||
#bevy_reflect_path::ReflectKind::Struct
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
|
||||
#bevy_reflect_path::ReflectRef::Struct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
|
||||
#bevy_reflect_path::ReflectMut::Struct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_owned(self: #FQBox<Self>) -> #bevy_reflect_path::ReflectOwned {
|
||||
#bevy_reflect_path::ReflectOwned::Struct(self)
|
||||
}
|
||||
|
|
|
@ -112,11 +112,11 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
|
|||
_ => #FQOption::None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn field_len(&self) -> usize {
|
||||
#field_count
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter {
|
||||
#bevy_reflect_path::TupleStructFieldIter::new(self)
|
||||
}
|
||||
|
@ -177,28 +177,36 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
|
||||
fn try_apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
|
||||
if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = #bevy_reflect_path::Reflect::reflect_ref(value) {
|
||||
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::TupleStruct::iter_fields(struct_value)) {
|
||||
#bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value));
|
||||
if let #FQOption::Some(v) = #bevy_reflect_path::TupleStruct::field_mut(self, i) {
|
||||
#bevy_reflect_path::Reflect::try_apply(v, value)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply non-TupleStruct type to TupleStruct type.");
|
||||
return #FQResult::Err(
|
||||
#bevy_reflect_path::ApplyError::MismatchedKinds {
|
||||
from_kind: #bevy_reflect_path::Reflect::reflect_kind(value),
|
||||
to_kind: #bevy_reflect_path::ReflectKind::TupleStruct,
|
||||
}
|
||||
);
|
||||
}
|
||||
#FQResult::Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
|
||||
#bevy_reflect_path::ReflectKind::TupleStruct
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
|
||||
#bevy_reflect_path::ReflectRef::TupleStruct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
|
||||
#bevy_reflect_path::ReflectMut::TupleStruct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_owned(self: #FQBox<Self>) -> #bevy_reflect_path::ReflectOwned {
|
||||
#bevy_reflect_path::ReflectOwned::TupleStruct(self)
|
||||
}
|
||||
|
|
|
@ -85,14 +85,20 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
|
|||
#FQBox::new(#FQClone::clone(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
|
||||
let value = #bevy_reflect_path::Reflect::as_any(value);
|
||||
if let #FQOption::Some(value) = <dyn #FQAny>::downcast_ref::<Self>(value) {
|
||||
#[inline]
|
||||
fn try_apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
|
||||
let any = #bevy_reflect_path::Reflect::as_any(value);
|
||||
if let #FQOption::Some(value) = <dyn #FQAny>::downcast_ref::<Self>(any) {
|
||||
*self = #FQClone::clone(value);
|
||||
} else {
|
||||
panic!("Value is not {}.", <Self as #bevy_reflect_path::TypePath>::type_path());
|
||||
return #FQResult::Err(
|
||||
#bevy_reflect_path::ApplyError::MismatchedTypes {
|
||||
from_type: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(value)),
|
||||
to_type: ::core::convert::Into::into(<Self as #bevy_reflect_path::TypePath>::type_path()),
|
||||
}
|
||||
);
|
||||
}
|
||||
#FQResult::Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -101,18 +107,22 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
|
|||
#FQResult::Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
|
||||
#bevy_reflect_path::ReflectKind::Value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
|
||||
#bevy_reflect_path::ReflectRef::Value(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
|
||||
#bevy_reflect_path::ReflectMut::Value(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_owned(self: #FQBox<Self>) -> #bevy_reflect_path::ReflectOwned {
|
||||
#bevy_reflect_path::ReflectOwned::Value(self)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
self as bevy_reflect, utility::reflect_hasher, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, TypeInfo, TypePath, TypePathTable,
|
||||
self as bevy_reflect, utility::reflect_hasher, ApplyError, Reflect, ReflectKind, ReflectMut,
|
||||
ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
|
||||
};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use std::{
|
||||
|
@ -262,6 +262,10 @@ impl Reflect for DynamicArray {
|
|||
array_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
array_try_apply(self, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
|
@ -421,6 +425,38 @@ pub fn array_apply<A: Array>(array: &mut A, reflect: &dyn Reflect) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Tries to apply the reflected [array](Array) data to the given [array](Array) and
|
||||
/// returns a Result.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * Returns an [`ApplyError::DifferentSize`] if the two arrays have differing lengths.
|
||||
/// * Returns an [`ApplyError::MismatchedKinds`] if the reflected value is not a
|
||||
/// [valid array](ReflectRef::Array).
|
||||
/// * Returns any error that is generated while applying elements to each other.
|
||||
///
|
||||
#[inline]
|
||||
pub fn array_try_apply<A: Array>(array: &mut A, reflect: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::Array(reflect_array) = reflect.reflect_ref() {
|
||||
if array.len() != reflect_array.len() {
|
||||
return Err(ApplyError::DifferentSize {
|
||||
from_size: reflect_array.len(),
|
||||
to_size: array.len(),
|
||||
});
|
||||
}
|
||||
for (i, value) in reflect_array.iter().enumerate() {
|
||||
let v = array.get_mut(i).unwrap();
|
||||
v.try_apply(value)?;
|
||||
}
|
||||
} else {
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: reflect.reflect_kind(),
|
||||
to_kind: ReflectKind::Array,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compares two [arrays](Array) (one concrete and one reflected) to see if they
|
||||
/// are equal.
|
||||
///
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use bevy_reflect_derive::impl_type_path;
|
||||
|
||||
use crate::{
|
||||
self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, DynamicStruct, DynamicTuple,
|
||||
Enum, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo,
|
||||
VariantFieldIter, VariantType,
|
||||
self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, ApplyError, DynamicStruct,
|
||||
DynamicTuple, Enum, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple,
|
||||
TypeInfo, VariantFieldIter, VariantType,
|
||||
};
|
||||
use std::any::Any;
|
||||
use std::fmt::Formatter;
|
||||
|
@ -324,7 +324,7 @@ impl Reflect for DynamicEnum {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::Enum(value) = value.reflect_ref() {
|
||||
if Enum::variant_name(self) == value.variant_name() {
|
||||
// Same variant -> just update fields
|
||||
|
@ -333,14 +333,14 @@ impl Reflect for DynamicEnum {
|
|||
for field in value.iter_fields() {
|
||||
let name = field.name().unwrap();
|
||||
if let Some(v) = Enum::field_mut(self, name) {
|
||||
v.apply(field.value());
|
||||
v.try_apply(field.value())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
VariantType::Tuple => {
|
||||
for (index, field) in value.iter_fields().enumerate() {
|
||||
if let Some(v) = Enum::field_at_mut(self, index) {
|
||||
v.apply(field.value());
|
||||
v.try_apply(field.value())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -369,8 +369,12 @@ impl Reflect for DynamicEnum {
|
|||
self.set_variant(value.variant_name(), dyn_variant);
|
||||
}
|
||||
} else {
|
||||
panic!("`{}` is not an enum", value.reflect_type_path());
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: value.reflect_kind(),
|
||||
to_kind: ReflectKind::Enum,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -307,8 +307,8 @@ impl<'a> VariantField<'a> {
|
|||
}
|
||||
|
||||
pub fn value(&self) -> &'a dyn Reflect {
|
||||
match self {
|
||||
Self::Struct(.., value) | Self::Tuple(value) => *value,
|
||||
match *self {
|
||||
Self::Struct(_, value) | Self::Tuple(value) => value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -283,7 +283,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "`bevy_reflect::DynamicTuple` is not an enum")]
|
||||
#[should_panic(
|
||||
expected = "called `Result::unwrap()` on an `Err` value: MismatchedKinds { from_kind: Tuple, to_kind: Enum }"
|
||||
)]
|
||||
fn applying_non_enum_should_panic() {
|
||||
let mut value = MyEnum::B(0, 0);
|
||||
let mut dyn_tuple = DynamicTuple::default();
|
||||
|
@ -291,6 +293,38 @@ mod tests {
|
|||
value.apply(&dyn_tuple);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_try_apply_should_detect_type_mismatch() {
|
||||
#[derive(Reflect, Debug, PartialEq)]
|
||||
enum MyEnumAnalogue {
|
||||
A(u32),
|
||||
B(usize, usize),
|
||||
C { foo: f32, bar: u8 },
|
||||
}
|
||||
|
||||
let mut target = MyEnumAnalogue::A(0);
|
||||
|
||||
// === Tuple === //
|
||||
let result = target.try_apply(&MyEnum::B(0, 1));
|
||||
assert!(
|
||||
matches!(result, Err(ApplyError::MismatchedTypes { .. })),
|
||||
"`result` was {result:?}"
|
||||
);
|
||||
|
||||
// === Struct === //
|
||||
target = MyEnumAnalogue::C { foo: 0.0, bar: 1 };
|
||||
let result = target.try_apply(&MyEnum::C {
|
||||
foo: 1.0,
|
||||
bar: true,
|
||||
});
|
||||
assert!(
|
||||
matches!(result, Err(ApplyError::MismatchedTypes { .. })),
|
||||
"`result` was {result:?}"
|
||||
);
|
||||
// Type mismatch should occur after partial application.
|
||||
assert_eq!(target, MyEnumAnalogue::C { foo: 1.0, bar: 1 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_skip_ignored_fields() {
|
||||
#[derive(Reflect, Debug, PartialEq)]
|
||||
|
|
|
@ -5,9 +5,9 @@ use std::any::Any;
|
|||
|
||||
use crate::utility::GenericTypeInfoCell;
|
||||
use crate::{
|
||||
self as bevy_reflect, FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter,
|
||||
Reflect, ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath,
|
||||
TypeRegistration, Typed,
|
||||
self as bevy_reflect, ApplyError, FromReflect, FromType, GetTypeRegistration, List, ListInfo,
|
||||
ListIter, Reflect, ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
|
||||
TypePath, TypeRegistration, Typed,
|
||||
};
|
||||
|
||||
impl<T: smallvec::Array + TypePath + Send + Sync> List for SmallVec<T>
|
||||
|
@ -113,6 +113,10 @@ where
|
|||
crate::list_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
crate::list_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use crate::std_traits::ReflectDefault;
|
||||
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, ReflectOwned, TypeRegistry};
|
||||
use crate::{
|
||||
impl_type_path, map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap,
|
||||
FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter,
|
||||
Reflect, ReflectDeserialize, ReflectKind, ReflectMut, ReflectRef, ReflectSerialize, TypeInfo,
|
||||
TypePath, TypeRegistration, Typed, ValueInfo,
|
||||
};
|
||||
|
||||
use crate::utility::{
|
||||
reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell,
|
||||
};
|
||||
use crate::{
|
||||
self as bevy_reflect, impl_type_path, map_apply, map_partial_eq, map_try_apply, ApplyError,
|
||||
Array, ArrayInfo, ArrayIter, DynamicMap, DynamicTypePath, FromReflect, FromType,
|
||||
GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, Reflect,
|
||||
ReflectDeserialize, ReflectFromPtr, ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, ReflectSerialize, TypeInfo, TypePath, TypeRegistration, TypeRegistry, Typed,
|
||||
ValueInfo,
|
||||
};
|
||||
use bevy_reflect_derive::{impl_reflect, impl_reflect_value};
|
||||
use std::fmt;
|
||||
use std::{
|
||||
|
@ -314,6 +314,10 @@ macro_rules! impl_reflect_for_veclike {
|
|||
crate::list_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
crate::list_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
@ -540,6 +544,10 @@ macro_rules! impl_reflect_for_hashmap {
|
|||
map_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
map_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
@ -765,6 +773,10 @@ where
|
|||
map_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
map_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
@ -909,6 +921,11 @@ impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> Reflect for [T
|
|||
crate::array_apply(self, value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
crate::array_try_apply(self, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
|
@ -1063,13 +1080,18 @@ impl Reflect for Cow<'static, str> {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
let value = value.as_any();
|
||||
if let Some(value) = value.downcast_ref::<Self>() {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
let any = value.as_any();
|
||||
if let Some(value) = any.downcast_ref::<Self>() {
|
||||
self.clone_from(value);
|
||||
} else {
|
||||
panic!("Value is not a {}.", Self::type_path());
|
||||
return Err(ApplyError::MismatchedTypes {
|
||||
from_type: value.reflect_type_path().into(),
|
||||
// If we invoke the reflect_type_path on self directly the borrow checker complains that the lifetime of self must outlive 'static
|
||||
to_type: Self::type_path().into(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
|
@ -1253,6 +1275,10 @@ impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> Reflect for Cow<'s
|
|||
crate::list_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
crate::list_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
@ -1349,13 +1375,17 @@ impl Reflect for &'static str {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
let value = value.as_any();
|
||||
if let Some(&value) = value.downcast_ref::<Self>() {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
let any = value.as_any();
|
||||
if let Some(&value) = any.downcast_ref::<Self>() {
|
||||
*self = value;
|
||||
} else {
|
||||
panic!("Value is not a {}.", Self::type_path());
|
||||
return Err(ApplyError::MismatchedTypes {
|
||||
from_type: value.reflect_type_path().into(),
|
||||
to_type: Self::type_path().into(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
|
@ -1451,12 +1481,16 @@ impl Reflect for &'static Path {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
let value = value.as_any();
|
||||
if let Some(&value) = value.downcast_ref::<Self>() {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
let any = value.as_any();
|
||||
if let Some(&value) = any.downcast_ref::<Self>() {
|
||||
*self = value;
|
||||
Ok(())
|
||||
} else {
|
||||
panic!("Value is not a {}.", Self::type_path());
|
||||
Err(ApplyError::MismatchedTypes {
|
||||
from_type: value.reflect_type_path().into(),
|
||||
to_type: <Self as DynamicTypePath>::reflect_type_path(self).into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1552,12 +1586,16 @@ impl Reflect for Cow<'static, Path> {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
let value = value.as_any();
|
||||
if let Some(value) = value.downcast_ref::<Self>() {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
let any = value.as_any();
|
||||
if let Some(value) = any.downcast_ref::<Self>() {
|
||||
self.clone_from(value);
|
||||
Ok(())
|
||||
} else {
|
||||
panic!("Value is not a {}.", Self::type_path());
|
||||
Err(ApplyError::MismatchedTypes {
|
||||
from_type: value.reflect_type_path().into(),
|
||||
to_type: <Self as DynamicTypePath>::reflect_type_path(self).into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1789,14 +1827,14 @@ mod tests {
|
|||
|
||||
// === None on None === //
|
||||
let patch = None::<Foo>;
|
||||
let mut value = None;
|
||||
let mut value = None::<Foo>;
|
||||
Reflect::apply(&mut value, &patch);
|
||||
|
||||
assert_eq!(patch, value, "None apply onto None");
|
||||
|
||||
// === Some on None === //
|
||||
let patch = Some(Foo(123));
|
||||
let mut value = None;
|
||||
let mut value = None::<Foo>;
|
||||
Reflect::apply(&mut value, &patch);
|
||||
|
||||
assert_eq!(patch, value, "Some apply onto None");
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
//! ## Patching
|
||||
//!
|
||||
//! These dynamic types come in handy when needing to apply multiple changes to another type.
|
||||
//! This is known as "patching" and is done using the [`Reflect::apply`] method.
|
||||
//! This is known as "patching" and is done using the [`Reflect::apply`] and [`Reflect::try_apply`] methods.
|
||||
//!
|
||||
//! ```
|
||||
//! # use bevy_reflect::{DynamicEnum, Reflect};
|
||||
|
@ -613,6 +613,54 @@ mod tests {
|
|||
use crate::serde::{ReflectDeserializer, ReflectSerializer};
|
||||
use crate::utility::GenericTypePathCell;
|
||||
|
||||
#[test]
|
||||
fn try_apply_should_detect_kinds() {
|
||||
#[derive(Reflect, Debug)]
|
||||
struct Struct {
|
||||
a: u32,
|
||||
b: f32,
|
||||
}
|
||||
|
||||
#[derive(Reflect, Debug)]
|
||||
enum Enum {
|
||||
A,
|
||||
B(u32),
|
||||
}
|
||||
|
||||
let mut struct_target = Struct {
|
||||
a: 0xDEADBEEF,
|
||||
b: 3.14,
|
||||
};
|
||||
|
||||
let mut enum_target = Enum::A;
|
||||
|
||||
let array_src = [8, 0, 8];
|
||||
|
||||
let result = struct_target.try_apply(&enum_target);
|
||||
assert!(
|
||||
matches!(
|
||||
result,
|
||||
Err(ApplyError::MismatchedKinds {
|
||||
from_kind: ReflectKind::Enum,
|
||||
to_kind: ReflectKind::Struct
|
||||
})
|
||||
),
|
||||
"result was {result:?}"
|
||||
);
|
||||
|
||||
let result = enum_target.try_apply(&array_src);
|
||||
assert!(
|
||||
matches!(
|
||||
result,
|
||||
Err(ApplyError::MismatchedKinds {
|
||||
from_kind: ReflectKind::Array,
|
||||
to_kind: ReflectKind::Enum
|
||||
})
|
||||
),
|
||||
"result was {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reflect_struct() {
|
||||
#[derive(Reflect)]
|
||||
|
|
|
@ -6,8 +6,8 @@ use bevy_reflect_derive::impl_type_path;
|
|||
|
||||
use crate::utility::reflect_hasher;
|
||||
use crate::{
|
||||
self as bevy_reflect, FromReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
|
||||
TypeInfo, TypePath, TypePathTable,
|
||||
self as bevy_reflect, ApplyError, FromReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, TypeInfo, TypePath, TypePathTable,
|
||||
};
|
||||
|
||||
/// A trait used to power [list-like] operations via [reflection].
|
||||
|
@ -312,6 +312,10 @@ impl Reflect for DynamicList {
|
|||
list_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
list_try_apply(self, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
|
@ -436,19 +440,40 @@ pub fn list_hash<L: List>(list: &L) -> Option<u64> {
|
|||
/// This function panics if `b` is not a list.
|
||||
#[inline]
|
||||
pub fn list_apply<L: List>(a: &mut L, b: &dyn Reflect) {
|
||||
if let Err(err) = list_try_apply(a, b) {
|
||||
panic!("{err}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to apply the elements of `b` to the corresponding elements of `a` and
|
||||
/// returns a Result.
|
||||
///
|
||||
/// If the length of `b` is greater than that of `a`, the excess elements of `b`
|
||||
/// are cloned and appended to `a`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns an [`ApplyError::MismatchedKinds`] if `b` is not a list or if
|
||||
/// applying elements to each other fails.
|
||||
#[inline]
|
||||
pub fn list_try_apply<L: List>(a: &mut L, b: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::List(list_value) = b.reflect_ref() {
|
||||
for (i, value) in list_value.iter().enumerate() {
|
||||
if i < a.len() {
|
||||
if let Some(v) = a.get_mut(i) {
|
||||
v.apply(value);
|
||||
v.try_apply(value)?;
|
||||
}
|
||||
} else {
|
||||
a.push(value.clone_value());
|
||||
List::push(a, value.clone_value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply a non-list type to a list type.");
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: b.reflect_kind(),
|
||||
to_kind: ReflectKind::List,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compares a [`List`] with a [`Reflect`] value.
|
||||
|
|
|
@ -5,8 +5,8 @@ use bevy_reflect_derive::impl_type_path;
|
|||
use bevy_utils::{Entry, HashMap};
|
||||
|
||||
use crate::{
|
||||
self as bevy_reflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
|
||||
TypePath, TypePathTable,
|
||||
self as bevy_reflect, ApplyError, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
|
||||
TypeInfo, TypePath, TypePathTable,
|
||||
};
|
||||
|
||||
/// A trait used to power [map-like] operations via [reflection].
|
||||
|
@ -345,6 +345,10 @@ impl Reflect for DynamicMap {
|
|||
map_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
map_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
@ -502,17 +506,37 @@ pub fn map_debug(dyn_map: &dyn Map, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|||
/// This function panics if `b` is not a reflected map.
|
||||
#[inline]
|
||||
pub fn map_apply<M: Map>(a: &mut M, b: &dyn Reflect) {
|
||||
if let Err(err) = map_try_apply(a, b) {
|
||||
panic!("{err}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to apply the elements of reflected map `b` to the corresponding elements of map `a`
|
||||
/// and returns a Result.
|
||||
///
|
||||
/// If a key from `b` does not exist in `a`, the value is cloned and inserted.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns an [`ApplyError::MismatchedKinds`] if `b` is not a reflected map or if
|
||||
/// applying elements to each other fails.
|
||||
#[inline]
|
||||
pub fn map_try_apply<M: Map>(a: &mut M, b: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::Map(map_value) = b.reflect_ref() {
|
||||
for (key, b_value) in map_value.iter() {
|
||||
if let Some(a_value) = a.get_mut(key) {
|
||||
a_value.apply(b_value);
|
||||
a_value.try_apply(b_value)?;
|
||||
} else {
|
||||
a.insert_boxed(key.clone_value(), b_value.clone_value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply a non-map type to a map type.");
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: b.reflect_kind(),
|
||||
to_kind: ReflectKind::Map,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -8,6 +8,8 @@ use std::{
|
|||
fmt::Debug,
|
||||
};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::utility::NonGenericTypeInfoCell;
|
||||
|
||||
macro_rules! impl_reflect_enum {
|
||||
|
@ -99,6 +101,42 @@ pub enum ReflectOwned {
|
|||
}
|
||||
impl_reflect_enum!(ReflectOwned);
|
||||
|
||||
/// A enumeration of all error outcomes that might happen when running [`try_apply`](Reflect::try_apply).
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ApplyError {
|
||||
#[error("attempted to apply `{from_kind}` to `{to_kind}`")]
|
||||
/// Attempted to apply the wrong [kind](ReflectKind) to a type, e.g. a struct to a enum.
|
||||
MismatchedKinds {
|
||||
from_kind: ReflectKind,
|
||||
to_kind: ReflectKind,
|
||||
},
|
||||
|
||||
#[error("enum variant `{variant_name}` doesn't have a field named `{field_name}`")]
|
||||
/// Enum variant that we tried to apply to was missing a field.
|
||||
MissingEnumField {
|
||||
variant_name: Box<str>,
|
||||
field_name: Box<str>,
|
||||
},
|
||||
|
||||
#[error("`{from_type}` is not `{to_type}`")]
|
||||
/// Tried to apply incompatible types.
|
||||
MismatchedTypes {
|
||||
from_type: Box<str>,
|
||||
to_type: Box<str>,
|
||||
},
|
||||
|
||||
#[error("attempted to apply type with {from_size} size to a type with {to_size} size")]
|
||||
/// Attempted to apply to types with mismatched sizez, e.g. a [u8; 4] to [u8; 3].
|
||||
DifferentSize { from_size: usize, to_size: usize },
|
||||
|
||||
#[error("variant with name `{variant_name}` does not exist on enum `{enum_name}`")]
|
||||
/// The enum we tried to apply to didn't contain a variant with the give name.
|
||||
UnknownVariant {
|
||||
enum_name: Box<str>,
|
||||
variant_name: Box<str>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A zero-sized enumuration of the "kinds" of a reflected type.
|
||||
///
|
||||
/// A [`ReflectKind`] is obtained via [`Reflect::reflect_kind`],
|
||||
|
@ -217,7 +255,20 @@ pub trait Reflect: DynamicTypePath + Any + Send + Sync {
|
|||
/// - If `T` is any complex type and the corresponding fields or elements of
|
||||
/// `self` and `value` are not of the same type.
|
||||
/// - If `T` is a value type and `self` cannot be downcast to `T`
|
||||
fn apply(&mut self, value: &dyn Reflect);
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
Reflect::try_apply(self, value).unwrap();
|
||||
}
|
||||
|
||||
/// Tries to [`apply`](Reflect::apply) a reflected value to this value.
|
||||
///
|
||||
/// Functions the same as the [`apply`](Reflect::apply) function but returns an error instead of
|
||||
/// panicking.
|
||||
///
|
||||
/// # Handling Errors
|
||||
///
|
||||
/// This function may leave `self` in a partially mutated state if a error was encountered on the way.
|
||||
/// consider maintaining a cloned instance of this data you can switch to if a error is encountered.
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError>;
|
||||
|
||||
/// Performs a type-checked assignment of a reflected value to this value.
|
||||
///
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
self as bevy_reflect, NamedField, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
|
||||
TypeInfo, TypePath, TypePathTable,
|
||||
self as bevy_reflect, ApplyError, NamedField, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, TypeInfo, TypePath, TypePathTable,
|
||||
};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use bevy_utils::HashMap;
|
||||
|
@ -420,19 +420,24 @@ impl Reflect for DynamicStruct {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::Struct(struct_value) = value.reflect_ref() {
|
||||
for (i, value) in struct_value.iter_fields().enumerate() {
|
||||
let name = struct_value.name_at(i).unwrap();
|
||||
if let Some(v) = self.field_mut(name) {
|
||||
v.apply(value);
|
||||
v.try_apply(value)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply non-struct type to struct type.");
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: value.reflect_kind(),
|
||||
to_kind: ReflectKind::Struct,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
|
|
@ -2,9 +2,9 @@ use bevy_reflect_derive::impl_type_path;
|
|||
use bevy_utils::all_tuples;
|
||||
|
||||
use crate::{
|
||||
self as bevy_reflect, utility::GenericTypePathCell, FromReflect, GetTypeRegistration, Reflect,
|
||||
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypeRegistration, TypeRegistry,
|
||||
Typed, UnnamedField,
|
||||
self as bevy_reflect, utility::GenericTypePathCell, ApplyError, FromReflect,
|
||||
GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath,
|
||||
TypeRegistration, TypeRegistry, Typed, UnnamedField,
|
||||
};
|
||||
use crate::{ReflectKind, TypePathTable};
|
||||
use std::any::{Any, TypeId};
|
||||
|
@ -369,6 +369,10 @@ impl Reflect for DynamicTuple {
|
|||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
tuple_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
||||
tuple_partial_eq(self, value)
|
||||
}
|
||||
|
@ -394,15 +398,33 @@ impl_type_path!((in bevy_reflect) DynamicTuple);
|
|||
/// This function panics if `b` is not a tuple.
|
||||
#[inline]
|
||||
pub fn tuple_apply<T: Tuple>(a: &mut T, b: &dyn Reflect) {
|
||||
if let Err(err) = tuple_try_apply(a, b) {
|
||||
panic!("{err}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to apply the elements of `b` to the corresponding elements of `a` and
|
||||
/// returns a Result.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns an [`ApplyError::MismatchedKinds`] if `b` is not a tuple or if
|
||||
/// applying elements to each other fails.
|
||||
#[inline]
|
||||
pub fn tuple_try_apply<T: Tuple>(a: &mut T, b: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::Tuple(tuple) = b.reflect_ref() {
|
||||
for (i, value) in tuple.iter_fields().enumerate() {
|
||||
if let Some(v) = a.field_mut(i) {
|
||||
v.apply(value);
|
||||
v.try_apply(value)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply non-Tuple type to Tuple type.");
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: b.reflect_kind(),
|
||||
to_kind: ReflectKind::Tuple,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compares a [`Tuple`] with a [`Reflect`] value.
|
||||
|
@ -545,6 +567,10 @@ macro_rules! impl_reflect_tuple {
|
|||
crate::tuple_apply(self, value);
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
crate::tuple_try_apply(self, value)
|
||||
}
|
||||
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use bevy_reflect_derive::impl_type_path;
|
||||
|
||||
use crate::{
|
||||
self as bevy_reflect, DynamicTuple, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
|
||||
Tuple, TypeInfo, TypePath, TypePathTable, UnnamedField,
|
||||
self as bevy_reflect, ApplyError, DynamicTuple, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, Tuple, TypeInfo, TypePath, TypePathTable, UnnamedField,
|
||||
};
|
||||
use std::any::{Any, TypeId};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
@ -329,18 +329,23 @@ impl Reflect for DynamicTupleStruct {
|
|||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, value: &dyn Reflect) {
|
||||
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
|
||||
if let ReflectRef::TupleStruct(tuple_struct) = value.reflect_ref() {
|
||||
for (i, value) in tuple_struct.iter_fields().enumerate() {
|
||||
if let Some(v) = self.field_mut(i) {
|
||||
v.apply(value);
|
||||
v.try_apply(value)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply non-TupleStruct type to TupleStruct type.");
|
||||
return Err(ApplyError::MismatchedKinds {
|
||||
from_kind: value.reflect_kind(),
|
||||
to_kind: ReflectKind::TupleStruct,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*self = value.take()?;
|
||||
Ok(())
|
||||
|
@ -371,6 +376,7 @@ impl Reflect for DynamicTupleStruct {
|
|||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
||||
tuple_struct_partial_eq(self, value)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use std::fmt::Debug;
|
|||
///
|
||||
/// ```
|
||||
/// # use std::any::Any;
|
||||
/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, TypePath, ValueInfo};
|
||||
/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, TypePath, ValueInfo, ApplyError};
|
||||
/// # use bevy_reflect::utility::NonGenericTypeInfoCell;
|
||||
/// use bevy_reflect::Typed;
|
||||
///
|
||||
|
@ -60,7 +60,7 @@ use std::fmt::Debug;
|
|||
/// # 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 try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> { 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!() }
|
||||
|
|
|
@ -147,22 +147,27 @@ pub trait DynamicTypePath {
|
|||
}
|
||||
|
||||
impl<T: TypePath> DynamicTypePath for T {
|
||||
#[inline]
|
||||
fn reflect_type_path(&self) -> &str {
|
||||
Self::type_path()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_short_type_path(&self) -> &str {
|
||||
Self::short_type_path()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_type_ident(&self) -> Option<&str> {
|
||||
Self::type_ident()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_crate_name(&self) -> Option<&str> {
|
||||
Self::crate_name()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_module_path(&self) -> Option<&str> {
|
||||
Self::module_path()
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ mod sealed {
|
|||
///
|
||||
/// ```
|
||||
/// # use std::any::Any;
|
||||
/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo, TypePath};
|
||||
/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo, TypePath, ApplyError};
|
||||
/// use bevy_reflect::utility::NonGenericTypeInfoCell;
|
||||
///
|
||||
/// struct Foo {
|
||||
|
@ -78,7 +78,7 @@ mod sealed {
|
|||
/// # 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 try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> { 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!() }
|
||||
|
@ -130,7 +130,7 @@ impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
|
|||
///
|
||||
/// ```
|
||||
/// # use std::any::Any;
|
||||
/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField};
|
||||
/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField, ApplyError};
|
||||
/// use bevy_reflect::utility::GenericTypeInfoCell;
|
||||
///
|
||||
/// struct Foo<T>(T);
|
||||
|
@ -157,7 +157,7 @@ impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
|
|||
/// # 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 try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> { 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!() }
|
||||
|
|
Loading…
Reference in a new issue