Skip to content

Commit 9c4ac7c

Browse files
BrezakfeyokorenhofMrGVSV
authored
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 <[email protected]> Co-authored-by: Gino Valente <[email protected]>
1 parent 2f87bb8 commit 9c4ac7c

21 files changed

+474
-115
lines changed

crates/bevy_reflect/derive/src/enum_utility.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub(crate) struct EnumVariantConstructors {
2121
pub(crate) fn get_variant_constructors(
2222
reflect_enum: &ReflectEnum,
2323
ref_value: &Ident,
24-
can_panic: bool,
24+
return_apply_error: bool,
2525
) -> EnumVariantConstructors {
2626
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
2727
let variant_count = reflect_enum.variants().len();
@@ -50,21 +50,6 @@ pub(crate) fn get_variant_constructors(
5050
_ => quote! { #FQDefault::default() }
5151
}
5252
} else {
53-
let (resolve_error, resolve_missing) = if can_panic {
54-
let field_ref_str = match &field_ident {
55-
Member::Named(ident) => format!("the field `{ident}`"),
56-
Member::Unnamed(index) => format!("the field at index {}", index.index)
57-
};
58-
let ty = field.data.ty.to_token_stream();
59-
60-
let on_error = format!("{field_ref_str} should be of type `{ty}`");
61-
let on_missing = format!("{field_ref_str} is required but could not be found");
62-
63-
(quote!(.expect(#on_error)), quote!(.expect(#on_missing)))
64-
} else {
65-
(quote!(?), quote!(?))
66-
};
67-
6853
let field_accessor = match &field.data.ident {
6954
Some(ident) => {
7055
let name = ident.to_string();
@@ -74,6 +59,31 @@ pub(crate) fn get_variant_constructors(
7459
};
7560
reflect_index += 1;
7661

62+
let (resolve_error, resolve_missing) = if return_apply_error {
63+
let field_ref_str = match &field_ident {
64+
Member::Named(ident) => format!("{ident}"),
65+
Member::Unnamed(index) => format!(".{}", index.index)
66+
};
67+
let ty = field.data.ty.to_token_stream();
68+
69+
(
70+
quote!(.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
71+
// The unwrap won't panic. By this point the #field_accessor would have been invoked once and any failure to
72+
// access the given field handled by the `resolve_missing` code bellow.
73+
from_type: ::core::convert::Into::into(
74+
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#FQOption::unwrap(#field_accessor))
75+
),
76+
to_type: ::core::convert::Into::into(<#ty as #bevy_reflect_path::TypePath>::type_path())
77+
})?),
78+
quote!(.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
79+
variant_name: ::core::convert::Into::into(#name),
80+
field_name: ::core::convert::Into::into(#field_ref_str)
81+
})?)
82+
)
83+
} else {
84+
(quote!(?), quote!(?))
85+
};
86+
7787
match &field.attrs.default {
7888
DefaultBehavior::Func(path) => quote! {
7989
if let #FQOption::Some(field) = #field_accessor {

crates/bevy_reflect/derive/src/impls/enums.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,20 +230,24 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
230230
}
231231

232232
#[inline]
233-
fn apply(&mut self, #ref_value: &dyn #bevy_reflect_path::Reflect) {
233+
fn try_apply(&mut self, #ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
234234
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) {
235235
if #bevy_reflect_path::Enum::variant_name(self) == #bevy_reflect_path::Enum::variant_name(#ref_value) {
236236
// Same variant -> just update fields
237237
match #bevy_reflect_path::Enum::variant_type(#ref_value) {
238238
#bevy_reflect_path::VariantType::Struct => {
239239
for field in #bevy_reflect_path::Enum::iter_fields(#ref_value) {
240240
let name = field.name().unwrap();
241-
#bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field.value()));
241+
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_mut(self, name) {
242+
#bevy_reflect_path::Reflect::try_apply(v, field.value())?;
243+
}
242244
}
243245
}
244246
#bevy_reflect_path::VariantType::Tuple => {
245247
for (index, field) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Enum::iter_fields(#ref_value)) {
246-
#bevy_reflect_path::Enum::field_at_mut(self, index).map(|v| v.apply(field.value()));
248+
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_at_mut(self, index) {
249+
#bevy_reflect_path::Reflect::try_apply(v, field.value())?;
250+
}
247251
}
248252
}
249253
_ => {}
@@ -254,12 +258,25 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
254258
#(#variant_names => {
255259
*self = #variant_constructors
256260
})*
257-
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
261+
name => {
262+
return #FQResult::Err(
263+
#bevy_reflect_path::ApplyError::UnknownVariant {
264+
enum_name: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(self)),
265+
variant_name: ::core::convert::Into::into(name),
266+
}
267+
);
268+
}
258269
}
259270
}
260271
} else {
261-
panic!("`{}` is not an enum", #bevy_reflect_path::DynamicTypePath::reflect_type_path(#ref_value));
272+
return #FQResult::Err(
273+
#bevy_reflect_path::ApplyError::MismatchedKinds {
274+
from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value),
275+
to_kind: #bevy_reflect_path::ReflectKind::Enum,
276+
}
277+
);
262278
}
279+
#FQResult::Ok(())
263280
}
264281

265282
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {

crates/bevy_reflect/derive/src/impls/structs.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,29 +208,37 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
208208
}
209209

210210
#[inline]
211-
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
211+
fn try_apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
212212
if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = #bevy_reflect_path::Reflect::reflect_ref(value) {
213213
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Struct::iter_fields(struct_value)) {
214214
let name = #bevy_reflect_path::Struct::name_at(struct_value, i).unwrap();
215-
#bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value));
215+
if let #FQOption::Some(v) = #bevy_reflect_path::Struct::field_mut(self, name) {
216+
#bevy_reflect_path::Reflect::try_apply(v, value)?;
217+
}
216218
}
217219
} else {
218-
panic!("Attempted to apply non-struct type to struct type.");
220+
return #FQResult::Err(
221+
#bevy_reflect_path::ApplyError::MismatchedKinds {
222+
from_kind: #bevy_reflect_path::Reflect::reflect_kind(value),
223+
to_kind: #bevy_reflect_path::ReflectKind::Struct
224+
}
225+
);
219226
}
227+
#FQResult::Ok(())
220228
}
221-
229+
#[inline]
222230
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
223231
#bevy_reflect_path::ReflectKind::Struct
224232
}
225-
233+
#[inline]
226234
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
227235
#bevy_reflect_path::ReflectRef::Struct(self)
228236
}
229-
237+
#[inline]
230238
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
231239
#bevy_reflect_path::ReflectMut::Struct(self)
232240
}
233-
241+
#[inline]
234242
fn reflect_owned(self: #FQBox<Self>) -> #bevy_reflect_path::ReflectOwned {
235243
#bevy_reflect_path::ReflectOwned::Struct(self)
236244
}

crates/bevy_reflect/derive/src/impls/tuple_structs.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
112112
_ => #FQOption::None,
113113
}
114114
}
115-
115+
#[inline]
116116
fn field_len(&self) -> usize {
117117
#field_count
118118
}
119-
119+
#[inline]
120120
fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter {
121121
#bevy_reflect_path::TupleStructFieldIter::new(self)
122122
}
@@ -177,28 +177,36 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
177177
}
178178

179179
#[inline]
180-
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
180+
fn try_apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
181181
if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = #bevy_reflect_path::Reflect::reflect_ref(value) {
182182
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::TupleStruct::iter_fields(struct_value)) {
183-
#bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value));
183+
if let #FQOption::Some(v) = #bevy_reflect_path::TupleStruct::field_mut(self, i) {
184+
#bevy_reflect_path::Reflect::try_apply(v, value)?;
185+
}
184186
}
185187
} else {
186-
panic!("Attempted to apply non-TupleStruct type to TupleStruct type.");
188+
return #FQResult::Err(
189+
#bevy_reflect_path::ApplyError::MismatchedKinds {
190+
from_kind: #bevy_reflect_path::Reflect::reflect_kind(value),
191+
to_kind: #bevy_reflect_path::ReflectKind::TupleStruct,
192+
}
193+
);
187194
}
195+
#FQResult::Ok(())
188196
}
189-
197+
#[inline]
190198
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
191199
#bevy_reflect_path::ReflectKind::TupleStruct
192200
}
193-
201+
#[inline]
194202
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
195203
#bevy_reflect_path::ReflectRef::TupleStruct(self)
196204
}
197-
205+
#[inline]
198206
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
199207
#bevy_reflect_path::ReflectMut::TupleStruct(self)
200208
}
201-
209+
#[inline]
202210
fn reflect_owned(self: #FQBox<Self>) -> #bevy_reflect_path::ReflectOwned {
203211
#bevy_reflect_path::ReflectOwned::TupleStruct(self)
204212
}

crates/bevy_reflect/derive/src/impls/values.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,20 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
8585
#FQBox::new(#FQClone::clone(self))
8686
}
8787

88-
#[inline]
89-
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
90-
let value = #bevy_reflect_path::Reflect::as_any(value);
91-
if let #FQOption::Some(value) = <dyn #FQAny>::downcast_ref::<Self>(value) {
88+
#[inline]
89+
fn try_apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
90+
let any = #bevy_reflect_path::Reflect::as_any(value);
91+
if let #FQOption::Some(value) = <dyn #FQAny>::downcast_ref::<Self>(any) {
9292
*self = #FQClone::clone(value);
9393
} else {
94-
panic!("Value is not {}.", <Self as #bevy_reflect_path::TypePath>::type_path());
94+
return #FQResult::Err(
95+
#bevy_reflect_path::ApplyError::MismatchedTypes {
96+
from_type: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(value)),
97+
to_type: ::core::convert::Into::into(<Self as #bevy_reflect_path::TypePath>::type_path()),
98+
}
99+
);
95100
}
101+
#FQResult::Ok(())
96102
}
97103

98104
#[inline]
@@ -101,18 +107,22 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
101107
#FQResult::Ok(())
102108
}
103109

110+
#[inline]
104111
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
105112
#bevy_reflect_path::ReflectKind::Value
106113
}
107114

115+
#[inline]
108116
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
109117
#bevy_reflect_path::ReflectRef::Value(self)
110118
}
111119

120+
#[inline]
112121
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
113122
#bevy_reflect_path::ReflectMut::Value(self)
114123
}
115124

125+
#[inline]
116126
fn reflect_owned(self: #FQBox<Self>) -> #bevy_reflect_path::ReflectOwned {
117127
#bevy_reflect_path::ReflectOwned::Value(self)
118128
}

crates/bevy_reflect/src/array.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
self as bevy_reflect, utility::reflect_hasher, Reflect, ReflectKind, ReflectMut, ReflectOwned,
3-
ReflectRef, TypeInfo, TypePath, TypePathTable,
2+
self as bevy_reflect, utility::reflect_hasher, ApplyError, Reflect, ReflectKind, ReflectMut,
3+
ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
44
};
55
use bevy_reflect_derive::impl_type_path;
66
use std::{
@@ -262,6 +262,10 @@ impl Reflect for DynamicArray {
262262
array_apply(self, value);
263263
}
264264

265+
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
266+
array_try_apply(self, value)
267+
}
268+
265269
#[inline]
266270
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
267271
*self = value.take()?;
@@ -421,6 +425,38 @@ pub fn array_apply<A: Array>(array: &mut A, reflect: &dyn Reflect) {
421425
}
422426
}
423427

428+
/// Tries to apply the reflected [array](Array) data to the given [array](Array) and
429+
/// returns a Result.
430+
///
431+
/// # Errors
432+
///
433+
/// * Returns an [`ApplyError::DifferentSize`] if the two arrays have differing lengths.
434+
/// * Returns an [`ApplyError::MismatchedKinds`] if the reflected value is not a
435+
/// [valid array](ReflectRef::Array).
436+
/// * Returns any error that is generated while applying elements to each other.
437+
///
438+
#[inline]
439+
pub fn array_try_apply<A: Array>(array: &mut A, reflect: &dyn Reflect) -> Result<(), ApplyError> {
440+
if let ReflectRef::Array(reflect_array) = reflect.reflect_ref() {
441+
if array.len() != reflect_array.len() {
442+
return Err(ApplyError::DifferentSize {
443+
from_size: reflect_array.len(),
444+
to_size: array.len(),
445+
});
446+
}
447+
for (i, value) in reflect_array.iter().enumerate() {
448+
let v = array.get_mut(i).unwrap();
449+
v.try_apply(value)?;
450+
}
451+
} else {
452+
return Err(ApplyError::MismatchedKinds {
453+
from_kind: reflect.reflect_kind(),
454+
to_kind: ReflectKind::Array,
455+
});
456+
}
457+
Ok(())
458+
}
459+
424460
/// Compares two [arrays](Array) (one concrete and one reflected) to see if they
425461
/// are equal.
426462
///

crates/bevy_reflect/src/enums/dynamic_enum.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use bevy_reflect_derive::impl_type_path;
22

33
use crate::{
4-
self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, DynamicStruct, DynamicTuple,
5-
Enum, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo,
6-
VariantFieldIter, VariantType,
4+
self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, ApplyError, DynamicStruct,
5+
DynamicTuple, Enum, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple,
6+
TypeInfo, VariantFieldIter, VariantType,
77
};
88
use std::any::Any;
99
use std::fmt::Formatter;
@@ -324,7 +324,7 @@ impl Reflect for DynamicEnum {
324324
}
325325

326326
#[inline]
327-
fn apply(&mut self, value: &dyn Reflect) {
327+
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
328328
if let ReflectRef::Enum(value) = value.reflect_ref() {
329329
if Enum::variant_name(self) == value.variant_name() {
330330
// Same variant -> just update fields
@@ -333,14 +333,14 @@ impl Reflect for DynamicEnum {
333333
for field in value.iter_fields() {
334334
let name = field.name().unwrap();
335335
if let Some(v) = Enum::field_mut(self, name) {
336-
v.apply(field.value());
336+
v.try_apply(field.value())?;
337337
}
338338
}
339339
}
340340
VariantType::Tuple => {
341341
for (index, field) in value.iter_fields().enumerate() {
342342
if let Some(v) = Enum::field_at_mut(self, index) {
343-
v.apply(field.value());
343+
v.try_apply(field.value())?;
344344
}
345345
}
346346
}
@@ -369,8 +369,12 @@ impl Reflect for DynamicEnum {
369369
self.set_variant(value.variant_name(), dyn_variant);
370370
}
371371
} else {
372-
panic!("`{}` is not an enum", value.reflect_type_path());
372+
return Err(ApplyError::MismatchedKinds {
373+
from_kind: value.reflect_kind(),
374+
to_kind: ReflectKind::Enum,
375+
});
373376
}
377+
Ok(())
374378
}
375379

376380
#[inline]

crates/bevy_reflect/src/enums/enum_trait.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,8 @@ impl<'a> VariantField<'a> {
307307
}
308308

309309
pub fn value(&self) -> &'a dyn Reflect {
310-
match self {
311-
Self::Struct(.., value) | Self::Tuple(value) => *value,
310+
match *self {
311+
Self::Struct(_, value) | Self::Tuple(value) => value,
312312
}
313313
}
314314
}

0 commit comments

Comments
 (0)