diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index b8b94c693358d..a44f4f8d95bb1 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -1,4 +1,4 @@ -use crate::fq_std::FQDefault; +use crate::fq_std::{FQBox, FQDefault}; use crate::{ derive_data::{EnumVariantFields, ReflectEnum}, utility::ident_or_index, @@ -53,18 +53,62 @@ pub(crate) fn get_variant_constructors( ); quote!(.expect(#type_err_message)) } else { - quote!(?) + match &field.data.ident { + Some(ident) => { + let name = ident.to_string(); + quote!(.map_err(|err| #bevy_reflect_path::FromReflectError::NamedFieldError { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + field: #name, + source: #FQBox::new(err), + })?) + }, + None => quote!(.map_err(|err| #bevy_reflect_path::FromReflectError::UnnamedFieldError { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + index: #reflect_index, + source: #FQBox::new(err), + })?) + } }; let field_accessor = match &field.data.ident { Some(ident) => { let name = ident.to_string(); - quote!(.field(#name)) + if can_panic { + quote!(.field(#name)) + } else { + quote!(.field(#name) + .ok_or_else(|| #bevy_reflect_path::FromReflectError::MissingNamedField { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + field: #name, + }) + ) + } + } + None => if can_panic { + quote!(.field_at(#reflect_index)) + } else { + quote!(.field_at(#reflect_index) + .ok_or_else(|| #bevy_reflect_path::FromReflectError::MissingUnnamedField { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + index: #reflect_index, + }) + ) } - None => quote!(.field_at(#reflect_index)), }; reflect_index += 1; let missing_field_err_message = format!("the field {error_repr} was not declared"); - let accessor = quote!(#field_accessor .expect(#missing_field_err_message)); + let accessor = if can_panic { + quote!(#field_accessor .expect(#missing_field_err_message)) + } else { + quote!(#field_accessor?) + }; quote! { #bevy_reflect_path::FromReflect::from_reflect(#ref_value #accessor) #unwrapper diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs b/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs index 7a4e8e78bdc16..9faf9e7d2a46c 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/fq_std.rs @@ -36,6 +36,7 @@ use quote::{quote, ToTokens}; pub(crate) struct FQAny; pub(crate) struct FQBox; pub(crate) struct FQClone; +pub(crate) struct FQCow; pub(crate) struct FQDefault; pub(crate) struct FQOption; pub(crate) struct FQResult; @@ -58,6 +59,12 @@ impl ToTokens for FQClone { } } +impl ToTokens for FQCow { + fn to_tokens(&self, tokens: &mut TokenStream) { + quote!(::std::borrow::Cow).to_tokens(tokens); + } +} + impl ToTokens for FQDefault { fn to_tokens(&self, tokens: &mut TokenStream) { quote!(::core::default::Default).to_tokens(tokens); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index aab2545b54989..a2832470a09ba 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -2,7 +2,7 @@ use crate::container_attributes::REFLECT_DEFAULT; use crate::derive_data::ReflectEnum; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::field_attributes::DefaultBehavior; -use crate::fq_std::{FQAny, FQClone, FQDefault, FQOption}; +use crate::fq_std::{FQAny, FQBox, FQClone, FQCow, FQDefault, FQResult}; use crate::{ReflectMeta, ReflectStruct}; use proc_macro::TokenStream; use proc_macro2::Span; @@ -26,8 +26,15 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption { - #FQOption::Some(#FQClone::clone(::downcast_ref::<#type_name #ty_generics>(::as_any(reflect))?)) + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQResult { + #FQResult::Ok(#FQClone::clone( + ::downcast_ref::<#type_name #ty_generics>(::as_any(reflect)) + .ok_or_else(|| #bevy_reflect_path::FromReflectError::InvalidType { + from_type: #bevy_reflect_path::Reflect::get_type_info(reflect), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(reflect), + to_type: ::type_info(), + })? + )) } } }) @@ -35,7 +42,9 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { /// Implements `FromReflect` for the given enum type pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { - let fqoption = FQOption.into_token_stream(); + let fqresult = FQResult.into_token_stream(); + let fqbox = FQBox.into_token_stream(); + let fqcow = FQCow.into_token_stream(); let type_name = reflect_enum.meta().type_name(); let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); @@ -50,14 +59,31 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { reflect_enum.meta().generics().split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { - fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQOption { + fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQResult { 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) { - #(#variant_names => #fqoption::Some(#variant_constructors),)* - name => panic!("variant with name `{}` does not exist on enum `{}`", name, ::core::any::type_name::()), + #(#variant_names => (|| #fqresult::Ok(#variant_constructors))().map_err(|err| { + #bevy_reflect_path::FromReflectError::VariantError { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + variant: #fqcow::Borrowed(#variant_names), + source: #fqbox::new(err), + } + }),)* + name => #FQResult::Err(#bevy_reflect_path::FromReflectError::MissingVariant { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + variant: #fqcow::Owned(name.to_string()), + }), } } else { - #FQOption::None + #FQResult::Err(#bevy_reflect_path::FromReflectError::InvalidType { + from_type: #bevy_reflect_path::Reflect::get_type_info(#ref_value), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(#ref_value), + to_type: ::type_info(), + }) } } } @@ -75,7 +101,7 @@ impl MemberValuePair { } fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> TokenStream { - let fqoption = FQOption.into_token_stream(); + let fqresult = FQResult.into_token_stream(); let struct_name = reflect_struct.meta().type_name(); let generics = reflect_struct.meta().generics(); @@ -92,23 +118,29 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token let MemberValuePair(active_members, active_values) = get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple); + let error = quote!(#bevy_reflect_path::FromReflectError::InvalidType { + from_type: #bevy_reflect_path::Reflect::get_type_info(reflect), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(reflect), + to_type: ::type_info(), + }); + let constructor = if reflect_struct.meta().traits().contains(REFLECT_DEFAULT) { quote!( let mut __this: Self = #FQDefault::default(); #( - if let #fqoption::Some(__field) = #active_values() { + if let #fqresult::Ok(__field) = #active_values() { // Iff field exists -> use its value __this.#active_members = __field; } )* - #FQOption::Some(__this) + #FQResult::Ok(__this) ) } else { let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct, is_tuple); quote!( - #FQOption::Some( + #FQResult::Ok( Self { #(#active_members: #active_values()?,)* #(#ignored_members: #ignored_values,)* @@ -134,11 +166,11 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQResult { if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = #bevy_reflect_path::Reflect::reflect_ref(reflect) { #constructor } else { - #FQOption::None + #FQResult::Err(#error) } } } @@ -187,31 +219,65 @@ fn get_active_fields( let accessor = get_field_accessor(field.data, field.index, is_tuple); let ty = field.data.ty.clone(); + let missing_error = if is_tuple { + quote!(|| #bevy_reflect_path::FromReflectError::MissingUnnamedField { + from_type: #bevy_reflect_path::Reflect::get_type_info(reflect), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(reflect), + to_type: ::type_info(), + index: #accessor, + }) + } else { + quote!(|| #bevy_reflect_path::FromReflectError::MissingNamedField { + from_type: #bevy_reflect_path::Reflect::get_type_info(reflect), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(reflect), + to_type: ::type_info(), + field: #accessor, + }) + }; + let get_field = quote! { - #bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor) + #bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor).ok_or_else(#missing_error) + }; + + let error = if is_tuple { + quote!(|err| #bevy_reflect_path::FromReflectError::UnnamedFieldError { + from_type: #bevy_reflect_path::Reflect::get_type_info(reflect), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(reflect), + to_type: ::type_info(), + index: #accessor, + source: #FQBox::new(err), + }) + } else { + quote!(|err| #bevy_reflect_path::FromReflectError::NamedFieldError { + from_type: #bevy_reflect_path::Reflect::get_type_info(reflect), + from_kind: #bevy_reflect_path::Reflect::reflect_kind(reflect), + to_type: ::type_info(), + field: #accessor, + source: #FQBox::new(err), + }) }; let value = match &field.attrs.default { DefaultBehavior::Func(path) => quote! { (|| - if let #FQOption::Some(field) = #get_field { - <#ty as #bevy_reflect_path::FromReflect>::from_reflect(field) + if let #FQResult::Ok(field) = #get_field { + <#ty as #bevy_reflect_path::FromReflect>::from_reflect(field).map_err(#error) } else { - #FQOption::Some(#path()) + #FQResult::Ok(#path()) } ) }, DefaultBehavior::Default => quote! { (|| - if let #FQOption::Some(field) = #get_field { - <#ty as #bevy_reflect_path::FromReflect>::from_reflect(field) + if let #FQResult::Ok(field) = #get_field { + <#ty as #bevy_reflect_path::FromReflect>::from_reflect(field).map_err(#error) } else { - #FQOption::Some(#FQDefault::default()) + #FQResult::Ok(#FQDefault::default()) } ) }, DefaultBehavior::Required => quote! { - (|| <#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)) + (|| <#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?).map_err(#error)) }, }; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index ce163092fac5a..e80aa56a00af8 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -262,6 +262,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } + fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind { + #bevy_reflect_path::ReflectKind::Enum + } + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { #bevy_reflect_path::ReflectRef::Enum(self) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index eefffd3ec0506..c2ef406764ead 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -224,6 +224,10 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { } } + fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind { + #bevy_reflect_path::ReflectKind::Struct + } + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { #bevy_reflect_path::ReflectRef::Struct(self) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index da187a3ca8a0b..c6a2eedd95feb 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -185,6 +185,10 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { } } + fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind { + #bevy_reflect_path::ReflectKind::TupleStruct + } + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { #bevy_reflect_path::ReflectRef::TupleStruct(self) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 5d87027d1a25a..7d764525f7f5d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -101,6 +101,10 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #FQResult::Ok(()) } + fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind { + #bevy_reflect_path::ReflectKind::Value + } + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { #bevy_reflect_path::ReflectRef::Value(self) } diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 438a135c040a3..158c5cb5aef2a 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,6 +1,6 @@ use crate::{ - utility::NonGenericTypeInfoCell, DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, - TypeInfo, Typed, + utility::NonGenericTypeInfoCell, DynamicInfo, Reflect, ReflectKind, ReflectMut, ReflectOwned, + ReflectRef, TypeInfo, Typed, }; use std::{ any::{Any, TypeId}, @@ -222,6 +222,11 @@ impl Reflect for DynamicArray { Ok(()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Array + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::Array(self) diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index fd3a8585b763f..3ff0520d76c46 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -1,7 +1,7 @@ use crate::utility::NonGenericTypeInfoCell; use crate::{ enum_debug, enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, DynamicTuple, Enum, - Reflect, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo, Typed, + Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo, Typed, VariantFieldIter, VariantType, }; use std::any::Any; @@ -386,6 +386,11 @@ impl Reflect for DynamicEnum { Ok(()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Enum + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::Enum(self) diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index 41c4c3582bd3a..9d8bdd800049a 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -1,4 +1,6 @@ -use crate::{FromType, Reflect}; +use crate::{FromType, Reflect, ReflectKind, TypeInfo}; +use std::borrow::Cow; +use thiserror::Error; /// A trait for types which can be constructed from a reflected type. /// @@ -12,7 +14,7 @@ use crate::{FromType, Reflect}; /// reflected value. pub trait FromReflect: Reflect + Sized { /// Constructs a concrete instance of `Self` from a reflected value. - fn from_reflect(reflect: &dyn Reflect) -> Option; + fn from_reflect(reflect: &dyn Reflect) -> Result; /// Attempts to downcast the given value to `Self` using, /// constructing the value using [`from_reflect`] if that fails. @@ -24,14 +26,12 @@ pub trait FromReflect: Reflect + Sized { /// [`from_reflect`]: Self::from_reflect /// [`DynamicStruct`]: crate::DynamicStruct /// [`DynamicList`]: crate::DynamicList - fn take_from_reflect(reflect: Box) -> Result> { - match reflect.take::() { - Ok(value) => Ok(value), - Err(value) => match Self::from_reflect(value.as_ref()) { - None => Err(value), - Some(value) => Ok(value), - }, - } + fn take_from_reflect( + reflect: Box, + ) -> Result, FromReflectError)> { + reflect + .take::() + .or_else(|value| Self::from_reflect(value.as_ref()).map_err(|err| (value, err))) } } @@ -87,7 +87,7 @@ pub trait FromReflect: Reflect + Sized { /// [`DynamicEnum`]: crate::DynamicEnum #[derive(Clone)] pub struct ReflectFromReflect { - from_reflect: fn(&dyn Reflect) -> Option>, + from_reflect: fn(&dyn Reflect) -> Result, FromReflectError>, } impl ReflectFromReflect { @@ -96,7 +96,10 @@ impl ReflectFromReflect { /// This will convert the object to a concrete type if it wasn't already, and return /// the value as `Box`. #[allow(clippy::wrong_self_convention)] - pub fn from_reflect(&self, reflect_value: &dyn Reflect) -> Option> { + pub fn from_reflect<'a>( + &'a self, + reflect_value: &'a dyn Reflect, + ) -> Result, FromReflectError> { (self.from_reflect)(reflect_value) } } @@ -110,3 +113,628 @@ impl FromType for ReflectFromReflect { } } } + +/// An Error for failed conversion of reflected type to original type in [`FromReflect::from_reflect`]. +/// +/// In the error message, the kind of the source type may have a prefix "(Dynamic)" indicating that the +/// source is dynamic, i.e., [`DynamicStruct`], [`DynamicList`], etc. +/// +/// Within variants `NamedFieldError`, `UnnamedFieldError`, `IndexError`, `VariantError`, `KeyError` and +/// `ValueError`; [`Error::source`] must be used to trace the underlying error. +/// +/// [`DynamicStruct`]: crate::DynamicStruct +/// [`DynamicList`]: crate::DynamicList +/// [`Error::source`]: std::error::Error::source +#[derive(Error, Debug)] +pub enum FromReflectError { + /// The source and target types are of different types or [kinds](ReflectKind). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` of kind {} due to mismatched types or kinds", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), self.display_to_kind())] + InvalidType { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + }, + + /// The source and target types have different lengths. + /// + /// This error is given by types of [kind](ReflectKind) [`Array`](crate::Array). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to source type having length of {} and target type having length of {}", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .from_len, .to_len)] + InvalidLength { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Length of the source type. + from_len: usize, + + /// Length of the target type. + to_len: usize, + }, + + /// The source type did not have a field with name given by the parameter `field`. + /// + /// This error is given by types of [kind](ReflectKind) [`Struct`](crate::Struct) and + /// [`Enum`](crate::Enum). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to a missing field `{}`", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .field)] + MissingNamedField { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Name of missing field in source type. + field: &'static str, + }, + + /// The source type did not have a field at index given by the parameter `index`. + /// + /// This error is given by types of [kind](ReflectKind) [`TupleStruct`](crate::TupleStruct), + /// [`Tuple`](crate::Tuple) and [`Enum`](crate::Enum). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to a missing field at index {}", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .index)] + MissingUnnamedField { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Index of missing field in source type. + index: usize, + }, + + /// The target type did not have a variant with name given by the parameter `variant`. + /// + /// This error is given by types of [kind](ReflectKind) [`Enum`](crate::Enum). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to a missing variant `{}`", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .variant)] + MissingVariant { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Name of missing variant in target type. + variant: Cow<'static, str>, + }, + + /// An error has occurred in conversion of a field with name given by the parameter `field`. + /// + /// Use [`Error::source`](std::error::Error::source) to get the underlying error. + /// + /// This error is given by types of [kind](ReflectKind) [`Struct`](crate::Struct) and + /// [`Enum`](crate::Enum). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to an error in the field `{}`", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .field)] + NamedFieldError { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Name of field where error occurred. + field: &'static str, + + /// Underlying error in conversion of field. + source: Box, + }, + + /// An error has occurred in conversion of a field at index given by the parameter `index`. + /// + /// Use [`Error::source`](std::error::Error::source) to get the underlying error. + /// + /// This error is given by types of [kind](ReflectKind) [`TupleStruct`](crate::TupleStruct), + /// [`Tuple`](crate::Tuple) and [`Enum`](crate::Enum). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to an error in the field at index {}", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .index)] + UnnamedFieldError { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Index of field where error occurred. + index: usize, + + /// Underlying error in conversion of field. + source: Box, + }, + + /// An error has occurred in conversion of a value at index given by the parameter `index`. + /// + /// Use [`Error::source`](std::error::Error::source) to get the underlying error. + /// + /// This error is given by types of [kind](ReflectKind) [`List`](crate::List) and + /// [`Array`](crate::Array). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to an error in the value at index `{}`", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .index)] + IndexError { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Index of value where error occurred. + index: usize, + + /// Underlying error in conversion of value at the index. + source: Box, + }, + + /// An error has occurred in conversion of a variant with name given by the parameter `variant`. + /// + /// Use [`Error::source`](std::error::Error::source) to get the underlying error. + /// + /// This error is given by types of [kind](ReflectKind) [`Enum`](crate::Enum). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to an error in the variant `{}`", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name(), .variant)] + VariantError { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Name of variant where error occurred. + variant: Cow<'static, str>, + + /// Underlying error in conversion of variant. + source: Box, + }, + + /// An error has occurred in conversion of a key of Map. + /// + /// Use [`Error::source`](std::error::Error::source) to get the underlying error. + /// + /// This error is given by types of [kind](ReflectKind) [`Map`](crate::Map). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to an error in a key of the Map", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name())] + KeyError { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Underlying error in conversion of a key of Map. + source: Box, + }, + + /// An error has occurred in conversion of a value of Map. + /// + /// Use [`Error::source`](std::error::Error::source) to get the underlying error. + /// + /// This error is given by types of [kind](ReflectKind) [`Map`](crate::Map). + #[error("The reflected type `{}` of kind {} cannot be converted to type `{}` due to an error in a value of the Map", + .from_type.type_name(), self.display_from_kind(), .to_type.type_name())] + ValueError { + /// [`TypeInfo`] of the source type. + from_type: &'static TypeInfo, + + /// [`ReflectKind`] of the source type. + from_kind: ReflectKind, + + /// [`TypeInfo`] of the target type. + to_type: &'static TypeInfo, + + /// Underlying error in conversion of a value of Map. + source: Box, + }, +} + +impl FromReflectError { + /// Returns the [`TypeInfo`] of the source type. + pub fn from_type(&self) -> &'static TypeInfo { + match self { + Self::InvalidType { from_type, .. } + | Self::InvalidLength { from_type, .. } + | Self::MissingNamedField { from_type, .. } + | Self::MissingUnnamedField { from_type, .. } + | Self::MissingVariant { from_type, .. } + | Self::NamedFieldError { from_type, .. } + | Self::UnnamedFieldError { from_type, .. } + | Self::IndexError { from_type, .. } + | Self::VariantError { from_type, .. } + | Self::KeyError { from_type, .. } + | Self::ValueError { from_type, .. } => from_type, + } + } + + /// Returns the [`TypeInfo`] of the target type. + pub fn to_type(&self) -> &'static TypeInfo { + match self { + Self::InvalidType { to_type, .. } + | Self::InvalidLength { to_type, .. } + | Self::MissingNamedField { to_type, .. } + | Self::MissingUnnamedField { to_type, .. } + | Self::MissingVariant { to_type, .. } + | Self::NamedFieldError { to_type, .. } + | Self::UnnamedFieldError { to_type, .. } + | Self::IndexError { to_type, .. } + | Self::VariantError { to_type, .. } + | Self::KeyError { to_type, .. } + | Self::ValueError { to_type, .. } => to_type, + } + } + + /// Returns the [`ReflectKind`] of the source type. + pub fn from_kind(&self) -> ReflectKind { + *match self { + Self::InvalidType { from_kind, .. } + | Self::InvalidLength { from_kind, .. } + | Self::MissingNamedField { from_kind, .. } + | Self::MissingUnnamedField { from_kind, .. } + | Self::MissingVariant { from_kind, .. } + | Self::NamedFieldError { from_kind, .. } + | Self::UnnamedFieldError { from_kind, .. } + | Self::IndexError { from_kind, .. } + | Self::VariantError { from_kind, .. } + | Self::KeyError { from_kind, .. } + | Self::ValueError { from_kind, .. } => from_kind, + } + } + + /// Returns the [kind](ReflectKind) of source type for display purposes. + fn display_from_kind(&self) -> String { + let prefix = if let TypeInfo::Dynamic(_) = self.from_type() { + "(Dynamic)" + } else { + "" + }; + + format!("{}{:?}", prefix, self.from_kind()) + } + + /// Returns the [kind](ReflectKind) of target type for display purposes. + fn display_to_kind(&self) -> &str { + match self.to_type() { + TypeInfo::Struct(_) => "Struct", + TypeInfo::TupleStruct(_) => "TupleStruct", + TypeInfo::Tuple(_) => "Tuple", + TypeInfo::List(_) => "List", + TypeInfo::Array(_) => "Array", + TypeInfo::Map(_) => "Map", + TypeInfo::Enum(_) => "Enum", + TypeInfo::Value(_) => "Value", + TypeInfo::Dynamic(_) => "Dynamic", + } + } +} + +#[cfg(test)] +mod tests { + use crate as bevy_reflect; + use crate::{ + DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTupleStruct, DynamicVariant, + FromReflect, FromReflectError, Reflect, ReflectKind, TypeInfo, + }; + use bevy_utils::HashMap; + use std::borrow::Cow; + + #[test] + fn check_invalid_type() { + #[derive(Reflect, FromReflect)] + struct Rectangle { + height: i32, + width: i32, + } + + let result = Rectangle::from_reflect(&vec![1, 2, 3, 4, 5]); + + assert!( + matches!( + result, + Err(FromReflectError::InvalidType { + from_type: TypeInfo::List(_), + from_kind: ReflectKind::List, + to_type: TypeInfo::Struct(_), + }) + ), + "Incorrect error handling of FromReflectError::InvalidType" + ); + } + + #[test] + fn check_invalid_length() { + let result = <[i32; 10]>::from_reflect(&[1, 2, 3, 4, 5]); + + assert!( + matches!( + result, + Err(FromReflectError::InvalidLength { + from_type: TypeInfo::Array(_), + from_kind: ReflectKind::Array, + to_type: TypeInfo::Array(_), + from_len: 5, + to_len: 10, + }) + ), + "Incorrect error handling of FromReflectError::InvalidLength" + ); + } + + #[test] + fn check_missing_named_field() { + #[derive(Reflect, FromReflect)] + struct Rectangle { + height: i32, + width: i32, + } + + let mut dyn_struct = DynamicStruct::default(); + dyn_struct.insert("height", 5); + + let result = Rectangle::from_reflect(&dyn_struct); + + assert!( + matches!( + result, + Err(FromReflectError::MissingNamedField { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Struct, + to_type: TypeInfo::Struct(_), + field: "width", + }) + ), + "Incorrect error handling of FromReflectError::MissingNamedField" + ); + } + + #[test] + fn check_missing_unnamed_field() { + #[derive(Reflect, FromReflect)] + struct Rectangle(i32, i32); + + let mut dyn_tuple_struct = DynamicTupleStruct::default(); + dyn_tuple_struct.insert(5); + + let result = Rectangle::from_reflect(&dyn_tuple_struct); + + assert!( + matches!( + result, + Err(FromReflectError::MissingUnnamedField { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::TupleStruct, + to_type: TypeInfo::TupleStruct(_), + index: 1, + }) + ), + "Incorrect error handling of FromReflectError::MissingUnnamedField" + ); + } + + #[test] + fn check_missing_variant() { + #[derive(Reflect, FromReflect)] + enum Shape { + Point, + Circle(i32), + Rectangle { height: i32, width: i32 }, + } + + let dyn_enum = DynamicEnum::new("Shape", "None", DynamicVariant::Unit); + let result = Shape::from_reflect(&dyn_enum); + + assert!( + matches!( + result, + Err(FromReflectError::MissingVariant { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Enum, + to_type: TypeInfo::Enum(_), + variant: Cow::Owned(x), + }) if x.as_str() == "None" + ), + "Incorrect error handling of FromReflectError::MissingVariant" + ); + } + + #[test] + fn check_named_field_error() { + #[derive(Reflect, FromReflect)] + struct Rectangle { + height: i32, + width: i32, + } + + let mut dyn_struct = DynamicStruct::default(); + dyn_struct.insert("height", 5); + dyn_struct.insert("width", 3.2); + + let result = Rectangle::from_reflect(&dyn_struct); + + assert!( + matches!( + result, + Err(FromReflectError::NamedFieldError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Struct, + to_type: TypeInfo::Struct(_), + field: "width", + source, + }) if matches!(*source, FromReflectError::InvalidType { .. }) + ), + "Incorrect error handling of FromReflectError::NamedFieldError" + ); + } + + #[test] + fn check_unnamed_field_error() { + #[derive(Reflect, FromReflect)] + struct Rectangle(i32, i32); + + let mut dyn_tuple_struct = DynamicTupleStruct::default(); + dyn_tuple_struct.insert(5); + dyn_tuple_struct.insert(3.2); + + let result = Rectangle::from_reflect(&dyn_tuple_struct); + + assert!( + matches!( + result, + Err(FromReflectError::UnnamedFieldError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::TupleStruct, + to_type: TypeInfo::TupleStruct(_), + index: 1, + source, + }) if matches!(*source, FromReflectError::InvalidType { .. }) + ), + "Incorrect error handling of FromReflectError::UnnamedFieldError" + ); + } + + #[test] + fn check_index_error() { + #[derive(Reflect, FromReflect)] + struct Rectangle(i32, i32); + + let mut dyn_list = DynamicList::default(); + dyn_list.push(1); + dyn_list.push(2); + dyn_list.push(3.2); + + let result = Vec::::from_reflect(&dyn_list); + + assert!( + matches!( + result, + Err(FromReflectError::IndexError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::List, + to_type: TypeInfo::List(_), + index: 2, + source, + }) if matches!(*source, FromReflectError::InvalidType { .. }) + ), + "Incorrect error handling of FromReflectError::IndexError" + ); + } + + #[test] + fn check_variant_error() { + #[derive(Reflect, FromReflect)] + enum Shape { + Point, + Circle(i32), + Rectangle { height: i32, width: i32 }, + } + + let mut dyn_struct = DynamicStruct::default(); + dyn_struct.insert("height", 5); + dyn_struct.insert("width", 3.2); + let dyn_enum = DynamicEnum::new("Shape", "Rectangle", DynamicVariant::Struct(dyn_struct)); + + let result = Shape::from_reflect(&dyn_enum); + + assert!( + matches!( + result, + Err(FromReflectError::VariantError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Enum, + to_type: TypeInfo::Enum(_), + variant: Cow::Borrowed("Rectangle"), + source, + }) if matches!( + &*source, + FromReflectError::NamedFieldError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Enum, + to_type: TypeInfo::Enum(_), + field: "width", + source: inner_source, + } if matches!(**inner_source, FromReflectError::InvalidType { .. }) + ) + ), + "Incorrect error handling of FromReflectError::VariantError" + ); + } + + #[test] + fn check_key_error() { + let mut dyn_map = DynamicMap::default(); + dyn_map.insert(String::from("a"), 5); + dyn_map.insert(9, 2); + + let result = HashMap::::from_reflect(&dyn_map); + + assert!( + matches!( + result, + Err(FromReflectError::KeyError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Map, + to_type: TypeInfo::Map(_), + source, + }) if matches!(*source, FromReflectError::InvalidType { .. }) + ), + "Incorrect error handling of FromReflectError::KeyError" + ); + } + + #[test] + fn check_value_error() { + let mut dyn_map = DynamicMap::default(); + dyn_map.insert(String::from("a"), 5); + dyn_map.insert(String::from("b"), 3.2); + + let result = HashMap::::from_reflect(&dyn_map); + + assert!( + matches!( + result, + Err(FromReflectError::ValueError { + from_type: TypeInfo::Dynamic(_), + from_kind: ReflectKind::Map, + to_type: TypeInfo::Map(_), + source, + }) if matches!(*source, FromReflectError::InvalidType { .. }) + ), + "Incorrect error handling of FromReflectError::ValueError" + ); + } +} diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 4a4c64e4ff9dd..736961fcd6dcf 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -3,8 +3,9 @@ use std::any::Any; use crate::utility::GenericTypeInfoCell; use crate::{ - Array, ArrayIter, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Reflect, - ReflectFromPtr, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed, + Array, ArrayIter, FromReflect, FromReflectError, FromType, GetTypeRegistration, List, ListInfo, + Reflect, ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + TypeRegistration, Typed, }; impl Array for SmallVec @@ -51,7 +52,7 @@ where { fn insert(&mut self, index: usize, value: Box) { let value = value.take::().unwrap_or_else(|value| { - ::Item::from_reflect(&*value).unwrap_or_else(|| { + ::Item::from_reflect(&*value).unwrap_or_else(|_| { panic!( "Attempted to insert invalid value of type {}.", value.type_name() @@ -67,7 +68,7 @@ where fn push(&mut self, value: Box) { let value = value.take::().unwrap_or_else(|value| { - ::Item::from_reflect(&*value).unwrap_or_else(|| { + ::Item::from_reflect(&*value).unwrap_or_else(|_| { panic!( "Attempted to push invalid value of type {}.", value.type_name() @@ -127,6 +128,10 @@ where Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::List + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::List(self) } @@ -162,15 +167,27 @@ impl FromReflect for SmallVec where T::Item: FromReflect, { - fn from_reflect(reflect: &dyn Reflect) -> Option { + fn from_reflect(reflect: &dyn Reflect) -> Result { if let ReflectRef::List(ref_list) = reflect.reflect_ref() { let mut new_list = Self::with_capacity(ref_list.len()); - for field in ref_list.iter() { - new_list.push(::Item::from_reflect(field)?); + for (idx, field) in ref_list.iter().enumerate() { + new_list.push(::Item::from_reflect(field).map_err( + |err| FromReflectError::IndexError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: idx, + source: Box::new(err), + }, + )?); } - Some(new_list) + Ok(new_list) } else { - None + Err(FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + }) } } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 9b7bbf8ff77fa..81a2d96197da7 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,14 +1,13 @@ use crate::std_traits::ReflectDefault; +use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; use crate::{self as bevy_reflect, ReflectFromPtr, 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, + EnumInfo, FromReflect, FromReflectError, FromType, GetTypeRegistration, List, ListInfo, Map, + MapInfo, MapIter, Reflect, ReflectDeserialize, ReflectKind, ReflectMut, ReflectRef, + ReflectSerialize, TupleVariantInfo, TypeInfo, TypeRegistration, Typed, UnitVariantInfo, + UnnamedField, ValueInfo, VariantFieldIter, VariantInfo, VariantType, }; - -use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; use bevy_utils::{Duration, Instant}; use bevy_utils::{HashMap, HashSet}; @@ -215,7 +214,7 @@ macro_rules! impl_reflect_for_veclike { impl List for $ty { fn insert(&mut self, index: usize, value: Box) { let value = value.take::().unwrap_or_else(|value| { - T::from_reflect(&*value).unwrap_or_else(|| { + T::from_reflect(&*value).unwrap_or_else(|_| { panic!( "Attempted to insert invalid value of type {}.", value.type_name() @@ -230,7 +229,7 @@ macro_rules! impl_reflect_for_veclike { } fn push(&mut self, value: Box) { - let value = T::take_from_reflect(value).unwrap_or_else(|value| { + let value = T::take_from_reflect(value).unwrap_or_else(|(value, _)| { panic!( "Attempted to push invalid value of type {}.", value.type_name() @@ -286,6 +285,10 @@ macro_rules! impl_reflect_for_veclike { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::List + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::List(self) } @@ -327,15 +330,28 @@ macro_rules! impl_reflect_for_veclike { } impl FromReflect for $ty { - fn from_reflect(reflect: &dyn Reflect) -> Option { + fn from_reflect(reflect: &dyn Reflect) -> Result { if let ReflectRef::List(ref_list) = reflect.reflect_ref() { let mut new_list = Self::with_capacity(ref_list.len()); - for field in ref_list.iter() { - $push(&mut new_list, T::from_reflect(field)?); + for (idx, field) in ref_list.iter().enumerate() { + $push( + &mut new_list, + T::from_reflect(field).map_err(|err| FromReflectError::IndexError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: idx, + source: Box::new(err), + })?, + ); } - Some(new_list) + Ok(new_list) } else { - None + Err(FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + }) } } } @@ -407,13 +423,13 @@ impl Map for HashMap { key: Box, value: Box, ) -> Option> { - let key = K::take_from_reflect(key).unwrap_or_else(|key| { + let key = K::take_from_reflect(key).unwrap_or_else(|(key, _)| { panic!( "Attempted to insert invalid key of type {}.", key.type_name() ) }); - let value = V::take_from_reflect(value).unwrap_or_else(|value| { + let value = V::take_from_reflect(value).unwrap_or_else(|(value, _)| { panic!( "Attempted to insert invalid value of type {}.", value.type_name() @@ -427,7 +443,7 @@ impl Map for HashMap { let mut from_reflect = None; key.downcast_ref::() .or_else(|| { - from_reflect = K::from_reflect(key); + from_reflect = K::from_reflect(key).ok(); from_reflect.as_ref() }) .and_then(|key| self.remove(key)) @@ -478,6 +494,10 @@ impl Reflect for HashMap { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Map + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::Map(self) } @@ -519,17 +539,32 @@ where } impl FromReflect for HashMap { - fn from_reflect(reflect: &dyn Reflect) -> Option { + fn from_reflect(reflect: &dyn Reflect) -> Result { if let ReflectRef::Map(ref_map) = reflect.reflect_ref() { let mut new_map = Self::with_capacity(ref_map.len()); for (key, value) in ref_map.iter() { - let new_key = K::from_reflect(key)?; - let new_value = V::from_reflect(value)?; + let new_key = K::from_reflect(key).map_err(|err| FromReflectError::KeyError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + source: Box::new(err), + })?; + let new_value = + V::from_reflect(value).map_err(|err| FromReflectError::ValueError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + source: Box::new(err), + })?; new_map.insert(new_key, new_value); } - Some(new_map) + Ok(new_map) } else { - None + Err(FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + }) } } } @@ -617,6 +652,11 @@ impl Reflect for [T; N] { Ok(()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Array + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::Array(self) @@ -649,15 +689,36 @@ impl Reflect for [T; N] { } impl FromReflect for [T; N] { - fn from_reflect(reflect: &dyn Reflect) -> Option { + fn from_reflect(reflect: &dyn Reflect) -> Result { if let ReflectRef::Array(ref_array) = reflect.reflect_ref() { let mut temp_vec = Vec::with_capacity(ref_array.len()); - for field in ref_array.iter() { - temp_vec.push(T::from_reflect(field)?); + for (idx, field) in ref_array.iter().enumerate() { + temp_vec.push(T::from_reflect(field).map_err(|err| { + FromReflectError::IndexError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: idx, + source: Box::new(err), + } + })?); } - temp_vec.try_into().ok() + let temp_vec_len = temp_vec.len(); + temp_vec + .try_into() + .map_err(|_| FromReflectError::InvalidLength { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + from_len: temp_vec_len, + to_len: N, + }) } else { - None + Err(FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + }) } } } @@ -858,6 +919,10 @@ impl Reflect for Option { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Enum + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::Enum(self) } @@ -885,39 +950,56 @@ impl Reflect for Option { } impl FromReflect for Option { - fn from_reflect(reflect: &dyn Reflect) -> Option { + fn from_reflect(reflect: &dyn Reflect) -> Result { if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() { match dyn_enum.variant_name() { "Some" => { - let field = T::take_from_reflect( - dyn_enum - .field_at(0) - .unwrap_or_else(|| { - panic!( - "Field in `Some` variant of {} should exist", - std::any::type_name::>() - ) - }) - .clone_value(), - ) - .unwrap_or_else(|_| { - panic!( - "Field in `Some` variant of {} should be of type {}", - std::any::type_name::>(), - std::any::type_name::() + // The closure is only created to provide a scope for `?` operator. + let field = (|| { + T::take_from_reflect( + dyn_enum + .field_at(0) + .ok_or_else(|| FromReflectError::MissingUnnamedField { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: 0, + })? + .clone_value(), ) - }); - Some(Some(field)) + .map_err(|(_, err)| { + FromReflectError::UnnamedFieldError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: 0, + source: Box::new(err), + } + }) + })() + .map_err(|err| FromReflectError::VariantError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + variant: Cow::Borrowed("Some"), + source: Box::new(err), + })?; + Ok(Some(field)) } - "None" => Some(None), - name => panic!( - "variant with name `{}` does not exist on enum `{}`", - name, - std::any::type_name::() - ), + "None" => Ok(None), + name => Err(FromReflectError::MissingVariant { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + variant: Cow::Owned(name.to_string()), + }), } } else { - None + Err(FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + }) } } } @@ -984,6 +1066,10 @@ impl Reflect for Cow<'static, str> { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Value + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::Value(self) } @@ -1035,13 +1121,16 @@ impl GetTypeRegistration for Cow<'static, str> { } impl FromReflect for Cow<'static, str> { - fn from_reflect(reflect: &dyn crate::Reflect) -> Option { - Some( - reflect - .as_any() - .downcast_ref::>()? - .clone(), - ) + fn from_reflect(reflect: &dyn crate::Reflect) -> Result { + Ok(reflect + .as_any() + .downcast_ref::>() + .ok_or_else(|| FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + })? + .clone()) } } @@ -1092,6 +1181,10 @@ impl Reflect for &'static Path { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Value + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::Value(self) } @@ -1141,8 +1234,16 @@ impl GetTypeRegistration for &'static Path { } impl FromReflect for &'static Path { - fn from_reflect(reflect: &dyn crate::Reflect) -> Option { - reflect.as_any().downcast_ref::().copied() + fn from_reflect(reflect: &dyn crate::Reflect) -> Result { + reflect + .as_any() + .downcast_ref::() + .copied() + .ok_or_else(|| FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + }) } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index dcf2d85861722..2919b088dd9a3 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -308,9 +308,9 @@ mod tests { }; let dyn_struct = DynamicStruct::default(); - let my_struct = ::from_reflect(&dyn_struct); + let my_struct = ::from_reflect(&dyn_struct).unwrap(); - assert_eq!(Some(expected), my_struct); + assert_eq!(expected, my_struct); } #[test] @@ -338,9 +338,9 @@ mod tests { }; let dyn_struct = DynamicStruct::default(); - let my_struct = ::from_reflect(&dyn_struct); + let my_struct = ::from_reflect(&dyn_struct).unwrap(); - assert_eq!(Some(expected), my_struct); + assert_eq!(expected, my_struct); } #[test] diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 2d67a62f24140..7d6b80855f738 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -3,8 +3,8 @@ use std::fmt::{Debug, Formatter}; use crate::utility::NonGenericTypeInfoCell; use crate::{ - Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectOwned, - ReflectRef, TypeInfo, Typed, + Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, ReflectKind, ReflectMut, + ReflectOwned, ReflectRef, TypeInfo, Typed, }; /// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`]. @@ -278,6 +278,11 @@ impl Reflect for DynamicList { Ok(()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::List + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::List(self) diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index f5e4a8695f2a2..8270d1dd1c571 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -5,7 +5,9 @@ use std::hash::Hash; use bevy_utils::{Entry, HashMap}; use crate::utility::NonGenericTypeInfoCell; -use crate::{DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed}; +use crate::{ + DynamicInfo, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed, +}; /// An ordered mapping between [`Reflect`] values. /// @@ -311,6 +313,10 @@ impl Reflect for DynamicMap { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Map + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::Map(self) } diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 3857d8f3cd84b..90504aba3262c 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -11,7 +11,28 @@ use std::{ use crate::utility::NonGenericTypeInfoCell; pub use bevy_utils::AHasher as ReflectHasher; -/// An immutable enumeration of "kinds" of reflected type. +/// A simple enumeration of [kinds](ReflectKind) of type, without any associated object. +/// +/// All types implementing [`Reflect`] are categorized into "kinds". They help to group types that +/// behave similarly and provide methods specific to its kind. These kinds directly correspond to +/// the traits [`Struct`], [`TupleStruct`], [`Tuple`], [`List`], [`Array`], [`Map`] and [`Enum`]; +/// which means that a type implementing any one of the above traits will be of the corresponding +/// kind. All the remaining types will be `ReflectKind::Value`. +/// +/// A `ReflectKind` is obtained via [`Reflect::reflect_kind`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum ReflectKind { + Struct, + TupleStruct, + Tuple, + List, + Array, + Map, + Enum, + Value, +} + +/// An immutable enumeration of [kinds](ReflectKind) of reflected type. /// /// Each variant contains a trait object with methods specific to a kind of /// type. @@ -28,7 +49,7 @@ pub enum ReflectRef<'a> { Value(&'a dyn Reflect), } -/// A mutable enumeration of "kinds" of reflected type. +/// A mutable enumeration of [kinds](ReflectKind) of reflected type. /// /// Each variant contains a trait object with methods specific to a kind of /// type. @@ -45,7 +66,7 @@ pub enum ReflectMut<'a> { Value(&'a mut dyn Reflect), } -/// An owned enumeration of "kinds" of reflected type. +/// An owned enumeration of [kinds](ReflectKind) of reflected type. /// /// Each variant contains a trait object with methods specific to a kind of /// type. @@ -64,8 +85,8 @@ pub enum ReflectOwned { /// A reflected Rust type. /// -/// Methods for working with particular kinds of Rust type are available using the [`Array`], [`List`], -/// [`Map`], [`Tuple`], [`TupleStruct`], [`Struct`], and [`Enum`] subtraits. +/// Methods for working with particular [kinds](ReflectKind) of Rust type are available using +/// the [`Array`], [`List`], [`Map`], [`Tuple`], [`TupleStruct`], [`Struct`], and [`Enum`] subtraits. /// /// When using `#[derive(Reflect)]` on a struct, tuple struct or enum, the suitable subtrait for that /// type (`Struct`, `TupleStruct` or `Enum`) is derived automatically. @@ -127,7 +148,7 @@ pub trait Reflect: Any + Send + Sync { /// Note that `Reflect` must be implemented manually for [`List`]s and /// [`Map`]s in order to achieve the correct semantics, as derived /// implementations will have the semantics for [`Struct`], [`TupleStruct`], [`Enum`] - /// or none of the above depending on the kind of type. For lists and maps, use the + /// or none of the above depending on the [kind] of type. For lists and maps, use the /// [`list_apply`] and [`map_apply`] helper functions when implementing this method. /// /// [`list_apply`]: crate::list_apply @@ -136,11 +157,13 @@ pub trait Reflect: Any + Send + Sync { /// # Panics /// /// Derived implementations of this method will panic: - /// - If the type of `value` is not of the same kind as `T` (e.g. if `T` is + /// - If the type of `value` is not of the same [kind] as `T` (e.g. if `T` is /// a `List`, while `value` is a `Struct`). /// - 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` + /// + /// [kind]: ReflectKind fn apply(&mut self, value: &dyn Reflect); /// Performs a type-checked assignment of a reflected value to this value. @@ -149,17 +172,22 @@ pub trait Reflect: Any + Send + Sync { /// containing the trait object. fn set(&mut self, value: Box) -> Result<(), Box>; - /// Returns an enumeration of "kinds" of type. + /// Returns the value's [kind](ReflectKind). + /// + /// See [`ReflectKind`]. + fn reflect_kind(&self) -> ReflectKind; + + /// Returns an immutable enumeration of [kinds](ReflectKind) of type. /// /// See [`ReflectRef`]. fn reflect_ref(&self) -> ReflectRef; - /// Returns a mutable enumeration of "kinds" of type. + /// Returns a mutable enumeration of [kinds](ReflectKind) of type. /// /// See [`ReflectMut`]. fn reflect_mut(&mut self) -> ReflectMut; - /// Returns an owned enumeration of "kinds" of type. + /// Returns an owned enumeration of [kinds](ReflectKind) of type. /// /// See [`ReflectOwned`]. fn reflect_owned(self: Box) -> ReflectOwned; diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 4b8485cbb5b3e..896a765be756d 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,6 +1,7 @@ use crate::utility::NonGenericTypeInfoCell; use crate::{ - DynamicInfo, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed, + DynamicInfo, NamedField, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + Typed, }; use bevy_utils::{Entry, HashMap}; use std::fmt::{Debug, Formatter}; @@ -424,6 +425,11 @@ impl Reflect for DynamicStruct { Box::new(self.clone_dynamic()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Struct + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::Struct(self) diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index cebabaf3ffc9d..12a5b7ab386da 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -1,7 +1,7 @@ use crate::utility::NonGenericTypeInfoCell; use crate::{ - DynamicInfo, FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, - TypeInfo, TypeRegistration, Typed, UnnamedField, + DynamicInfo, FromReflect, FromReflectError, GetTypeRegistration, Reflect, ReflectKind, + ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed, UnnamedField, }; use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; @@ -336,6 +336,11 @@ impl Reflect for DynamicTuple { Box::new(self.clone_dynamic()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Tuple + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::Tuple(self) @@ -546,6 +551,10 @@ macro_rules! impl_reflect_tuple { Ok(()) } + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::Tuple + } + fn reflect_ref(&self) -> ReflectRef { ReflectRef::Tuple(self) } @@ -588,17 +597,35 @@ macro_rules! impl_reflect_tuple { impl<$($name: FromReflect),*> FromReflect for ($($name,)*) { - fn from_reflect(reflect: &dyn Reflect) -> Option { + fn from_reflect(reflect: &dyn Reflect) -> Result { if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() { - Some( + Ok( ( $( - <$name as FromReflect>::from_reflect(_ref_tuple.field($index)?)?, + <$name as FromReflect>::from_reflect( + _ref_tuple.field($index) + .ok_or_else(|| FromReflectError::MissingUnnamedField { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: $index, + })? + ).map_err(|err| FromReflectError::UnnamedFieldError { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info(), + index: $index, + source: Box::new(err), + })?, )* ) ) } else { - None + Err(FromReflectError::InvalidType { + from_type: reflect.get_type_info(), + from_kind: reflect.reflect_kind(), + to_type: Self::type_info() + }) } } } diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index e8c52bdadddbd..bd547b5a926ac 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,6 +1,7 @@ use crate::utility::NonGenericTypeInfoCell; use crate::{ - DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed, UnnamedField, + DynamicInfo, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed, + UnnamedField, }; use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; @@ -327,6 +328,11 @@ impl Reflect for DynamicTupleStruct { Box::new(self.clone_dynamic()) } + #[inline] + fn reflect_kind(&self) -> ReflectKind { + ReflectKind::TupleStruct + } + #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::TupleStruct(self) diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 42e011c14915f..18315a66f814b 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -23,7 +23,9 @@ use std::any::{Any, TypeId}; /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, ValueInfo}; +/// # use bevy_reflect::{ +/// # NamedField, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, ValueInfo +/// # }; /// # use bevy_reflect::utility::NonGenericTypeInfoCell; /// use bevy_reflect::Typed; /// @@ -58,6 +60,7 @@ use std::any::{Any, TypeId}; /// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } /// # fn apply(&mut self, value: &dyn Reflect) { todo!() } /// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } +/// # fn reflect_kind(&self) -> ReflectKind { todo!() } /// # fn reflect_ref(&self) -> ReflectRef { todo!() } /// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index 8474bcb6e8595..960281f36ed99 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -16,7 +16,9 @@ use std::any::{Any, TypeId}; /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo}; +/// # use bevy_reflect::{ +/// # NamedField, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo +/// # }; /// use bevy_reflect::utility::NonGenericTypeInfoCell; /// /// struct Foo { @@ -45,6 +47,7 @@ use std::any::{Any, TypeId}; /// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } /// # fn apply(&mut self, value: &dyn Reflect) { todo!() } /// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } +/// # fn reflect_kind(&self) -> ReflectKind { todo!() } /// # fn reflect_ref(&self) -> ReflectRef { todo!() } /// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } @@ -81,7 +84,10 @@ impl NonGenericTypeInfoCell { /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField}; +/// # use bevy_reflect::{ +/// # Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, +/// # TypeInfo, UnnamedField, +/// # }; /// use bevy_reflect::utility::GenericTypeInfoCell; /// /// struct Foo(T); @@ -108,6 +114,7 @@ impl NonGenericTypeInfoCell { /// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } /// # fn apply(&mut self, value: &dyn Reflect) { todo!() } /// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } +/// # fn reflect_kind(&self) -> ReflectKind { todo!() } /// # fn reflect_ref(&self) -> ReflectRef { todo!() } /// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() }