From 747fa780f5ce8ae2ffbb33e55f83a0c5f368d1f9 Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Wed, 6 May 2026 11:03:47 +0300 Subject: [PATCH] continue splitting `reflect` module --- fyrox-core/src/reflect/field.rs | 181 +++++++ fyrox-core/src/reflect/handle.rs | 34 ++ fyrox-core/src/reflect/inherit.rs | 59 +++ fyrox-core/src/reflect/mod.rs | 845 ++++++++++++------------------ 4 files changed, 595 insertions(+), 524 deletions(-) create mode 100644 fyrox-core/src/reflect/field.rs create mode 100644 fyrox-core/src/reflect/handle.rs create mode 100644 fyrox-core/src/reflect/inherit.rs diff --git a/fyrox-core/src/reflect/field.rs b/fyrox-core/src/reflect/field.rs new file mode 100644 index 000000000..28c90346c --- /dev/null +++ b/fyrox-core/src/reflect/field.rs @@ -0,0 +1,181 @@ +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use crate::reflect::{CastError, Reflect}; +use std::any::{Any, TypeId}; +use std::fmt; +use std::ops::Deref; + +/// A value of a field.. +pub trait FieldValue: Any + 'static { + /// Casts `self` to a `&dyn Any` + fn field_value_as_any_ref(&self) -> &dyn Any; + + /// Casts `self` to a `&mut dyn Any` + fn field_value_as_any_mut(&mut self) -> &mut dyn Any; + + fn field_value_as_reflect(&self) -> &dyn Reflect; + fn field_value_as_reflect_mut(&mut self) -> &mut dyn Reflect; + + fn type_name(&self) -> &'static str; +} + +impl FieldValue for T { + fn field_value_as_any_ref(&self) -> &dyn Any { + self + } + + fn field_value_as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn field_value_as_reflect(&self) -> &dyn Reflect { + self + } + + fn field_value_as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + fn type_name(&self) -> &'static str { + std::any::type_name::() + } +} + +#[derive(Debug)] +pub struct FieldMetadata<'s> { + /// A name of the property. + pub name: &'s str, + + /// A human-readable name of the property. + pub display_name: &'s str, + + /// Tag of the property. Could be used to group properties by a certain criteria or to find a + /// specific property by its tag. + pub tag: &'s str, + + /// Doc comment content. + pub doc: &'s str, + + /// A property is not meant to be edited. + pub read_only: bool, + + /// Only for dynamic collections (Vec, etc) - means that its size cannot be changed, however the + /// _items_ of the collection can still be changed. + pub immutable_collection: bool, + + /// A minimal value of the property. Works only with numeric properties! + pub min_value: Option, + + /// A minimal value of the property. Works only with numeric properties! + pub max_value: Option, + + /// A minimal value of the property. Works only with numeric properties! + pub step: Option, + + /// Maximum amount of decimal places for a numeric property. + pub precision: Option, +} + +pub struct FieldRef<'a, 'b> { + /// A reference to field's metadata. + pub metadata: &'a FieldMetadata<'b>, + + /// An reference to the actual value of the property. This is "non-mangled" reference, which + /// means that while `field/fields/field_mut/fields_mut` might return a reference to other value, + /// than the actual field, the `value` is guaranteed to be a reference to the real value. + pub value: &'a dyn FieldValue, +} + +impl<'b> Deref for FieldRef<'_, 'b> { + type Target = FieldMetadata<'b>; + + fn deref(&self) -> &Self::Target { + self.metadata + } +} + +impl FieldRef<'_, '_> { + /// Tries to cast a value to a given type. + pub fn cast_value(&self) -> Result<&T, CastError> { + match self.value.field_value_as_any_ref().downcast_ref::() { + Some(value) => Ok(value), + None => Err(CastError::TypeMismatch { + property_name: self.metadata.name.to_string(), + expected_type_id: TypeId::of::(), + actual_type_id: self.value.type_id(), + }), + } + } +} + +impl fmt::Debug for FieldRef<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FieldInfo") + .field("metadata", &self.metadata) + .field("value", &format_args!("{:?}", self.value as *const _)) + .finish() + } +} + +impl PartialEq for FieldRef<'_, '_> { + fn eq(&self, other: &Self) -> bool { + let value_ptr_a = self.value as *const _ as *const (); + let value_ptr_b = other.value as *const _ as *const (); + + std::ptr::eq(value_ptr_a, value_ptr_b) + } +} + +pub struct FieldMut<'a, 'b> { + /// A reference to field's metadata. + pub metadata: &'a FieldMetadata<'b>, + + /// An reference to the actual value of the property. This is "non-mangled" reference, which + /// means that while `field/fields/field_mut/fields_mut` might return a reference to other value, + /// than the actual field, the `value` is guaranteed to be a reference to the real value. + pub value: &'a mut dyn FieldValue, +} + +impl<'b> Deref for FieldMut<'_, 'b> { + type Target = FieldMetadata<'b>; + + fn deref(&self) -> &Self::Target { + self.metadata + } +} + +impl fmt::Debug for FieldMut<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FieldInfo") + .field("metadata", &self.metadata) + .field("value", &format_args!("{:?}", self.value as *const _)) + .finish() + } +} + +impl PartialEq for FieldMut<'_, '_> { + fn eq(&self, other: &Self) -> bool { + let value_ptr_a = self.value as *const _ as *const (); + let value_ptr_b = other.value as *const _ as *const (); + + std::ptr::eq(value_ptr_a, value_ptr_b) + } +} diff --git a/fyrox-core/src/reflect/handle.rs b/fyrox-core/src/reflect/handle.rs new file mode 100644 index 000000000..e6faa5119 --- /dev/null +++ b/fyrox-core/src/reflect/handle.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use crate::pool::ErasedHandle; +use crate::reflect::Reflect; +use std::any::TypeId; + +pub trait ReflectHandle: Reflect { + fn reflect_inner_type_id(&self) -> TypeId; + fn reflect_inner_type_name(&self) -> &'static str; + fn reflect_is_some(&self) -> bool; + fn reflect_set_index(&mut self, index: u32); + fn reflect_index(&self) -> u32; + fn reflect_set_generation(&mut self, generation: u32); + fn reflect_generation(&self) -> u32; + fn reflect_as_erased(&self) -> ErasedHandle; +} diff --git a/fyrox-core/src/reflect/inherit.rs b/fyrox-core/src/reflect/inherit.rs new file mode 100644 index 000000000..c98434a58 --- /dev/null +++ b/fyrox-core/src/reflect/inherit.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use crate::reflect::Reflect; +use crate::variable::{InheritError, VariableFlags}; +use std::any::TypeId; + +pub trait ReflectInheritableVariable: Reflect { + /// Tries to inherit a value from parent. It will succeed only if the current variable is + /// not marked as modified. + fn try_inherit( + &mut self, + parent: &dyn ReflectInheritableVariable, + ignored_types: &[TypeId], + ) -> Result>, InheritError>; + + /// Resets modified flag from the variable. + fn reset_modified_flag(&mut self); + + /// Returns current variable flags. + fn flags(&self) -> VariableFlags; + + fn set_flags(&mut self, flags: VariableFlags); + + /// Returns true if value was modified. + fn is_modified(&self) -> bool; + + /// Returns true if value equals to other's value. + fn value_equals(&self, other: &dyn ReflectInheritableVariable) -> bool; + + /// Clones self value. + fn clone_value_box(&self) -> Box; + + /// Marks value as modified, so its value won't be overwritten during property inheritance. + fn mark_modified(&mut self); + + /// Returns a mutable reference to wrapped value without marking the variable itself as modified. + fn inner_value_mut(&mut self) -> &mut dyn Reflect; + + /// Returns a shared reference to wrapped value without marking the variable itself as modified. + fn inner_value_ref(&self) -> &dyn Reflect; +} diff --git a/fyrox-core/src/reflect/mod.rs b/fyrox-core/src/reflect/mod.rs index 773208aef..40c72785a 100644 --- a/fyrox-core/src/reflect/mod.rs +++ b/fyrox-core/src/reflect/mod.rs @@ -23,26 +23,27 @@ mod array; mod error; mod external_impls; +mod field; +mod handle; +mod inherit; mod macros; mod map; mod std_impls; -use crate::{ - pool::ErasedHandle, - sstorage::ImmutableString, - variable::{InheritError, VariableFlags}, -}; +use crate::sstorage::ImmutableString; pub use fyrox_core_derive::Reflect; use std::{ any::{Any, TypeId}, - fmt::{self, Debug}, + fmt::Debug, mem::ManuallyDrop, - ops::Deref, }; pub use array::*; pub use error::*; +pub use field::*; +pub use handle::*; +pub use inherit::*; pub use macros::*; pub use map::*; @@ -53,163 +54,6 @@ pub mod prelude { }; } -/// A value of a field.. -pub trait FieldValue: Any + 'static { - /// Casts `self` to a `&dyn Any` - fn field_value_as_any_ref(&self) -> &dyn Any; - - /// Casts `self` to a `&mut dyn Any` - fn field_value_as_any_mut(&mut self) -> &mut dyn Any; - - fn field_value_as_reflect(&self) -> &dyn Reflect; - fn field_value_as_reflect_mut(&mut self) -> &mut dyn Reflect; - - fn type_name(&self) -> &'static str; -} - -impl FieldValue for T { - fn field_value_as_any_ref(&self) -> &dyn Any { - self - } - - fn field_value_as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn field_value_as_reflect(&self) -> &dyn Reflect { - self - } - - fn field_value_as_reflect_mut(&mut self) -> &mut dyn Reflect { - self - } - - fn type_name(&self) -> &'static str { - std::any::type_name::() - } -} - -#[derive(Debug)] -pub struct FieldMetadata<'s> { - /// A name of the property. - pub name: &'s str, - - /// A human-readable name of the property. - pub display_name: &'s str, - - /// Tag of the property. Could be used to group properties by a certain criteria or to find a - /// specific property by its tag. - pub tag: &'s str, - - /// Doc comment content. - pub doc: &'s str, - - /// A property is not meant to be edited. - pub read_only: bool, - - /// Only for dynamic collections (Vec, etc) - means that its size cannot be changed, however the - /// _items_ of the collection can still be changed. - pub immutable_collection: bool, - - /// A minimal value of the property. Works only with numeric properties! - pub min_value: Option, - - /// A minimal value of the property. Works only with numeric properties! - pub max_value: Option, - - /// A minimal value of the property. Works only with numeric properties! - pub step: Option, - - /// Maximum amount of decimal places for a numeric property. - pub precision: Option, -} - -pub struct FieldRef<'a, 'b> { - /// A reference to field's metadata. - pub metadata: &'a FieldMetadata<'b>, - - /// An reference to the actual value of the property. This is "non-mangled" reference, which - /// means that while `field/fields/field_mut/fields_mut` might return a reference to other value, - /// than the actual field, the `value` is guaranteed to be a reference to the real value. - pub value: &'a dyn FieldValue, -} - -impl<'b> Deref for FieldRef<'_, 'b> { - type Target = FieldMetadata<'b>; - - fn deref(&self) -> &Self::Target { - self.metadata - } -} - -impl FieldRef<'_, '_> { - /// Tries to cast a value to a given type. - pub fn cast_value(&self) -> Result<&T, CastError> { - match self.value.field_value_as_any_ref().downcast_ref::() { - Some(value) => Ok(value), - None => Err(CastError::TypeMismatch { - property_name: self.metadata.name.to_string(), - expected_type_id: TypeId::of::(), - actual_type_id: self.value.type_id(), - }), - } - } -} - -impl fmt::Debug for FieldRef<'_, '_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FieldInfo") - .field("metadata", &self.metadata) - .field("value", &format_args!("{:?}", self.value as *const _)) - .finish() - } -} - -impl PartialEq for FieldRef<'_, '_> { - fn eq(&self, other: &Self) -> bool { - let value_ptr_a = self.value as *const _ as *const (); - let value_ptr_b = other.value as *const _ as *const (); - - std::ptr::eq(value_ptr_a, value_ptr_b) - } -} - -pub struct FieldMut<'a, 'b> { - /// A reference to field's metadata. - pub metadata: &'a FieldMetadata<'b>, - - /// An reference to the actual value of the property. This is "non-mangled" reference, which - /// means that while `field/fields/field_mut/fields_mut` might return a reference to other value, - /// than the actual field, the `value` is guaranteed to be a reference to the real value. - pub value: &'a mut dyn FieldValue, -} - -impl<'b> Deref for FieldMut<'_, 'b> { - type Target = FieldMetadata<'b>; - - fn deref(&self) -> &Self::Target { - self.metadata - } -} - -impl fmt::Debug for FieldMut<'_, '_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FieldInfo") - .field("metadata", &self.metadata) - .field("value", &format_args!("{:?}", self.value as *const _)) - .finish() - } -} - -impl PartialEq for FieldMut<'_, '_> { - fn eq(&self, other: &Self) -> bool { - let value_ptr_a = self.value as *const _ as *const (); - let value_ptr_b = other.value as *const _ as *const (); - - std::ptr::eq(value_ptr_a, value_ptr_b) - } -} - /// A trait for runtime reflection. /// /// ## Code Generation @@ -408,366 +252,6 @@ pub trait Reflect: Any + Debug { } } -pub trait ReflectHandle: Reflect { - fn reflect_inner_type_id(&self) -> TypeId; - fn reflect_inner_type_name(&self) -> &'static str; - fn reflect_is_some(&self) -> bool; - fn reflect_set_index(&mut self, index: u32); - fn reflect_index(&self) -> u32; - fn reflect_set_generation(&mut self, generation: u32); - fn reflect_generation(&self) -> u32; - fn reflect_as_erased(&self) -> ErasedHandle; -} - -pub trait ReflectInheritableVariable: Reflect { - /// Tries to inherit a value from parent. It will succeed only if the current variable is - /// not marked as modified. - fn try_inherit( - &mut self, - parent: &dyn ReflectInheritableVariable, - ignored_types: &[TypeId], - ) -> Result>, InheritError>; - - /// Resets modified flag from the variable. - fn reset_modified_flag(&mut self); - - /// Returns current variable flags. - fn flags(&self) -> VariableFlags; - - fn set_flags(&mut self, flags: VariableFlags); - - /// Returns true if value was modified. - fn is_modified(&self) -> bool; - - /// Returns true if value equals to other's value. - fn value_equals(&self, other: &dyn ReflectInheritableVariable) -> bool; - - /// Clones self value. - fn clone_value_box(&self) -> Box; - - /// Marks value as modified, so its value won't be overwritten during property inheritance. - fn mark_modified(&mut self); - - /// Returns a mutable reference to wrapped value without marking the variable itself as modified. - fn inner_value_mut(&mut self) -> &mut dyn Reflect; - - /// Returns a shared reference to wrapped value without marking the variable itself as modified. - fn inner_value_ref(&self) -> &dyn Reflect; -} - -pub trait ResolvePath { - fn resolve_path<'p>( - &self, - path: &'p str, - func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), - ); - - fn resolve_path_mut<'p>( - &mut self, - path: &'p str, - func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), - ); - - fn get_resolve_path<'p, T: Reflect>( - &self, - path: &'p str, - func: &mut dyn FnMut(Result<&T, ReflectPathError<'p>>), - ) { - self.resolve_path(path, &mut |resolve_result| { - match resolve_result { - Ok(value) => { - value.downcast_ref(&mut |result| { - match result { - Some(value) => { - func(Ok(value)); - } - None => { - func(Err(ReflectPathError::InvalidDowncast)); - } - }; - }); - } - Err(err) => { - func(Err(err)); - } - }; - }) - } - - fn get_resolve_path_mut<'p, T: Reflect>( - &mut self, - path: &'p str, - func: &mut dyn FnMut(Result<&mut T, ReflectPathError<'p>>), - ) { - self.resolve_path_mut(path, &mut |result| match result { - Ok(value) => value.downcast_mut(&mut |result| match result { - Some(value) => func(Ok(value)), - None => func(Err(ReflectPathError::InvalidDowncast)), - }), - Err(err) => func(Err(err)), - }) - } -} - -impl ResolvePath for T { - fn resolve_path<'p>( - &self, - path: &'p str, - func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), - ) { - (self as &dyn Reflect).resolve_path(path, func) - } - - fn resolve_path_mut<'p>( - &mut self, - path: &'p str, - func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), - ) { - (self as &mut dyn Reflect).resolve_path_mut(path, func) - } -} - -/// Splits property path into individual components. -pub fn path_to_components(path: &str) -> Vec { - let mut components = Vec::new(); - let mut current_path = path; - while let Ok((component, sub_path)) = Component::next(current_path) { - if let Component::Field(field) = component { - if field.is_empty() { - break; - } - } - current_path = sub_path; - components.push(component); - } - components -} - -/// Helper methods over [`Reflect`] types -pub trait GetField { - fn get_field(&self, name: &str, func: &mut dyn FnMut(Option<&T>)); - - fn get_field_mut(&mut self, _name: &str, func: &mut dyn FnMut(Option<&mut T>)); -} - -impl GetField for R { - fn get_field(&self, name: &str, func: &mut dyn FnMut(Option<&T>)) { - self.field(name, &mut |field| match field { - None => func(None), - Some(reflect) => reflect.as_any(&mut |any| func(any.downcast_ref())), - }) - } - - fn get_field_mut(&mut self, name: &str, func: &mut dyn FnMut(Option<&mut T>)) { - self.field_mut(name, &mut |field| match field { - None => func(None), - Some(reflect) => reflect.as_any_mut(&mut |any| func(any.downcast_mut())), - }) - } -} - -// -------------------------------------------------------------------------------- -// impl dyn Trait -// -------------------------------------------------------------------------------- - -// SAFETY: String usage is safe in immutable contexts only. Calling `ManuallyDrop::drop` -// (running strings destructor) on the returned value will cause crash! -unsafe fn make_fake_string_from_slice(string: &str) -> ManuallyDrop { - ManuallyDrop::new(String::from_utf8_unchecked(Vec::from_raw_parts( - string.as_bytes().as_ptr() as *mut _, - string.len(), - string.len(), - ))) -} - -fn try_fetch_by_str_path_ref( - hash_map: &dyn ReflectHashMap, - path: &str, - func: &mut dyn FnMut(Option<&dyn Reflect>), -) { - // Create fake string here first, this is needed to avoid memory allocations.. - // SAFETY: We won't drop the fake string or mutate it. - let fake_string_key = unsafe { make_fake_string_from_slice(path) }; - - hash_map.reflect_get(&*fake_string_key, &mut |result| match result { - Some(value) => func(Some(value)), - None => hash_map.reflect_get(&ImmutableString::new(path) as &dyn Reflect, func), - }); -} - -fn try_fetch_by_str_path_mut( - hash_map: &mut dyn ReflectHashMap, - path: &str, - func: &mut dyn FnMut(Option<&mut dyn Reflect>), -) { - // Create fake string here first, this is needed to avoid memory allocations.. - // SAFETY: We won't drop the fake string or mutate it. - let fake_string_key = unsafe { make_fake_string_from_slice(path) }; - - let mut succeeded = true; - - hash_map.reflect_get_mut(&*fake_string_key, &mut |result| match result { - Some(value) => func(Some(value)), - None => succeeded = false, - }); - - if !succeeded { - hash_map.reflect_get_mut(&ImmutableString::new(path) as &dyn Reflect, func) - } -} - -/// Simple path parser / reflect path component -pub enum Component<'p> { - Field(&'p str), - Index(&'p str), -} - -impl<'p> Component<'p> { - fn next(mut path: &'p str) -> Result<(Self, &'p str), ReflectPathError<'p>> { - // Discard the first comma: - if path.bytes().next() == Some(b'.') { - path = &path[1..]; - } - - let mut bytes = path.bytes().enumerate(); - while let Some((i, b)) = bytes.next() { - if b == b'.' { - let (l, r) = path.split_at(i); - return Ok((Self::Field(l), &r[1..])); - } - - if b == b'[' { - if i != 0 { - // delimit the field access - let (l, r) = path.split_at(i); - return Ok((Self::Field(l), r)); - } - - // find ']' - if let Some((end, _)) = bytes.find(|(_, b)| *b == b']') { - let l = &path[1..end]; - let r = &path[end + 1..]; - return Ok((Self::Index(l), r)); - } else { - return Err(ReflectPathError::UnclosedBrackets { s: path }); - } - } - } - - // NOTE: the `path` can be empty - Ok((Self::Field(path), "")) - } - - fn resolve( - &self, - reflect: &dyn Reflect, - func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), - ) { - match self { - Self::Field(path) => reflect.field(path, &mut |field| { - func(field.ok_or(ReflectPathError::UnknownField { s: path })) - }), - Self::Index(path) => { - reflect.as_array(&mut |result| match result { - Some(array) => match path.parse::() { - Ok(index) => match array.reflect_index(index) { - None => func(Err(ReflectPathError::NoItemForIndex { s: path })), - Some(value) => func(Ok(value)), - }, - Err(_) => func(Err(ReflectPathError::InvalidIndexSyntax { s: path })), - }, - None => reflect.as_hash_map(&mut |result| match result { - Some(hash_map) => { - try_fetch_by_str_path_ref(hash_map, path, &mut |result| { - func(result.ok_or(ReflectPathError::NoItemForIndex { s: path })) - }) - } - None => func(Err(ReflectPathError::NotAnArray)), - }), - }); - } - } - } - - fn resolve_mut( - &self, - reflect: &mut dyn Reflect, - func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), - ) { - match self { - Self::Field(path) => reflect.field_mut(path, &mut |field| { - func(field.ok_or(ReflectPathError::UnknownField { s: path })) - }), - Self::Index(path) => { - let mut succeeded = true; - reflect.as_array_mut(&mut |array| match array { - Some(list) => match path.parse::() { - Ok(index) => match list.reflect_index_mut(index) { - None => func(Err(ReflectPathError::NoItemForIndex { s: path })), - Some(value) => func(Ok(value)), - }, - Err(_) => func(Err(ReflectPathError::InvalidIndexSyntax { s: path })), - }, - None => succeeded = false, - }); - - if !succeeded { - reflect.as_hash_map_mut(&mut |result| match result { - Some(hash_map) => { - try_fetch_by_str_path_mut(hash_map, path, &mut |result| { - func(result.ok_or(ReflectPathError::NoItemForIndex { s: path })) - }) - } - None => func(Err(ReflectPathError::NotAnArray)), - }) - } - } - } - } -} - -impl ResolvePath for dyn Reflect { - fn resolve_path<'p>( - &self, - path: &'p str, - func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), - ) { - match Component::next(path) { - Ok((component, r)) => component.resolve(self, &mut |result| match result { - Ok(child) => { - if r.is_empty() { - func(Ok(child)) - } else { - child.resolve_path(r, func) - } - } - Err(err) => func(Err(err)), - }), - Err(err) => func(Err(err)), - } - } - - fn resolve_path_mut<'p>( - &mut self, - path: &'p str, - func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), - ) { - match Component::next(path) { - Ok((component, r)) => component.resolve_mut(self, &mut |result| match result { - Ok(child) => { - if r.is_empty() { - func(Ok(child)) - } else { - child.resolve_path_mut(r, func) - } - } - Err(err) => func(Err(err)), - }), - Err(err) => func(Err(err)), - } - } -} - /// Type-erased API impl dyn Reflect { pub fn downcast(self: Box) -> Result, Box> { @@ -1096,6 +580,319 @@ impl dyn Reflect { } } +pub trait ResolvePath { + fn resolve_path<'p>( + &self, + path: &'p str, + func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), + ); + + fn resolve_path_mut<'p>( + &mut self, + path: &'p str, + func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), + ); + + fn get_resolve_path<'p, T: Reflect>( + &self, + path: &'p str, + func: &mut dyn FnMut(Result<&T, ReflectPathError<'p>>), + ) { + self.resolve_path(path, &mut |resolve_result| { + match resolve_result { + Ok(value) => { + value.downcast_ref(&mut |result| { + match result { + Some(value) => { + func(Ok(value)); + } + None => { + func(Err(ReflectPathError::InvalidDowncast)); + } + }; + }); + } + Err(err) => { + func(Err(err)); + } + }; + }) + } + + fn get_resolve_path_mut<'p, T: Reflect>( + &mut self, + path: &'p str, + func: &mut dyn FnMut(Result<&mut T, ReflectPathError<'p>>), + ) { + self.resolve_path_mut(path, &mut |result| match result { + Ok(value) => value.downcast_mut(&mut |result| match result { + Some(value) => func(Ok(value)), + None => func(Err(ReflectPathError::InvalidDowncast)), + }), + Err(err) => func(Err(err)), + }) + } +} + +impl ResolvePath for T { + fn resolve_path<'p>( + &self, + path: &'p str, + func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), + ) { + (self as &dyn Reflect).resolve_path(path, func) + } + + fn resolve_path_mut<'p>( + &mut self, + path: &'p str, + func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), + ) { + (self as &mut dyn Reflect).resolve_path_mut(path, func) + } +} + +/// Splits property path into individual components. +pub fn path_to_components(path: &str) -> Vec { + let mut components = Vec::new(); + let mut current_path = path; + while let Ok((component, sub_path)) = Component::next(current_path) { + if let Component::Field(field) = component { + if field.is_empty() { + break; + } + } + current_path = sub_path; + components.push(component); + } + components +} + +/// Helper methods over [`Reflect`] types +pub trait GetField { + fn get_field(&self, name: &str, func: &mut dyn FnMut(Option<&T>)); + + fn get_field_mut(&mut self, _name: &str, func: &mut dyn FnMut(Option<&mut T>)); +} + +impl GetField for R { + fn get_field(&self, name: &str, func: &mut dyn FnMut(Option<&T>)) { + self.field(name, &mut |field| match field { + None => func(None), + Some(reflect) => reflect.as_any(&mut |any| func(any.downcast_ref())), + }) + } + + fn get_field_mut(&mut self, name: &str, func: &mut dyn FnMut(Option<&mut T>)) { + self.field_mut(name, &mut |field| match field { + None => func(None), + Some(reflect) => reflect.as_any_mut(&mut |any| func(any.downcast_mut())), + }) + } +} + +// -------------------------------------------------------------------------------- +// impl dyn Trait +// -------------------------------------------------------------------------------- + +// SAFETY: String usage is safe in immutable contexts only. Calling `ManuallyDrop::drop` +// (running strings destructor) on the returned value will cause crash! +unsafe fn make_fake_string_from_slice(string: &str) -> ManuallyDrop { + ManuallyDrop::new(String::from_utf8_unchecked(Vec::from_raw_parts( + string.as_bytes().as_ptr() as *mut _, + string.len(), + string.len(), + ))) +} + +fn try_fetch_by_str_path_ref( + hash_map: &dyn ReflectHashMap, + path: &str, + func: &mut dyn FnMut(Option<&dyn Reflect>), +) { + // Create fake string here first, this is needed to avoid memory allocations.. + // SAFETY: We won't drop the fake string or mutate it. + let fake_string_key = unsafe { make_fake_string_from_slice(path) }; + + hash_map.reflect_get(&*fake_string_key, &mut |result| match result { + Some(value) => func(Some(value)), + None => hash_map.reflect_get(&ImmutableString::new(path) as &dyn Reflect, func), + }); +} + +fn try_fetch_by_str_path_mut( + hash_map: &mut dyn ReflectHashMap, + path: &str, + func: &mut dyn FnMut(Option<&mut dyn Reflect>), +) { + // Create fake string here first, this is needed to avoid memory allocations.. + // SAFETY: We won't drop the fake string or mutate it. + let fake_string_key = unsafe { make_fake_string_from_slice(path) }; + + let mut succeeded = true; + + hash_map.reflect_get_mut(&*fake_string_key, &mut |result| match result { + Some(value) => func(Some(value)), + None => succeeded = false, + }); + + if !succeeded { + hash_map.reflect_get_mut(&ImmutableString::new(path) as &dyn Reflect, func) + } +} + +/// Simple path parser / reflect path component +pub enum Component<'p> { + Field(&'p str), + Index(&'p str), +} + +impl<'p> Component<'p> { + fn next(mut path: &'p str) -> Result<(Self, &'p str), ReflectPathError<'p>> { + // Discard the first comma: + if path.bytes().next() == Some(b'.') { + path = &path[1..]; + } + + let mut bytes = path.bytes().enumerate(); + while let Some((i, b)) = bytes.next() { + if b == b'.' { + let (l, r) = path.split_at(i); + return Ok((Self::Field(l), &r[1..])); + } + + if b == b'[' { + if i != 0 { + // delimit the field access + let (l, r) = path.split_at(i); + return Ok((Self::Field(l), r)); + } + + // find ']' + if let Some((end, _)) = bytes.find(|(_, b)| *b == b']') { + let l = &path[1..end]; + let r = &path[end + 1..]; + return Ok((Self::Index(l), r)); + } else { + return Err(ReflectPathError::UnclosedBrackets { s: path }); + } + } + } + + // NOTE: the `path` can be empty + Ok((Self::Field(path), "")) + } + + fn resolve( + &self, + reflect: &dyn Reflect, + func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), + ) { + match self { + Self::Field(path) => reflect.field(path, &mut |field| { + func(field.ok_or(ReflectPathError::UnknownField { s: path })) + }), + Self::Index(path) => { + reflect.as_array(&mut |result| match result { + Some(array) => match path.parse::() { + Ok(index) => match array.reflect_index(index) { + None => func(Err(ReflectPathError::NoItemForIndex { s: path })), + Some(value) => func(Ok(value)), + }, + Err(_) => func(Err(ReflectPathError::InvalidIndexSyntax { s: path })), + }, + None => reflect.as_hash_map(&mut |result| match result { + Some(hash_map) => { + try_fetch_by_str_path_ref(hash_map, path, &mut |result| { + func(result.ok_or(ReflectPathError::NoItemForIndex { s: path })) + }) + } + None => func(Err(ReflectPathError::NotAnArray)), + }), + }); + } + } + } + + fn resolve_mut( + &self, + reflect: &mut dyn Reflect, + func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), + ) { + match self { + Self::Field(path) => reflect.field_mut(path, &mut |field| { + func(field.ok_or(ReflectPathError::UnknownField { s: path })) + }), + Self::Index(path) => { + let mut succeeded = true; + reflect.as_array_mut(&mut |array| match array { + Some(list) => match path.parse::() { + Ok(index) => match list.reflect_index_mut(index) { + None => func(Err(ReflectPathError::NoItemForIndex { s: path })), + Some(value) => func(Ok(value)), + }, + Err(_) => func(Err(ReflectPathError::InvalidIndexSyntax { s: path })), + }, + None => succeeded = false, + }); + + if !succeeded { + reflect.as_hash_map_mut(&mut |result| match result { + Some(hash_map) => { + try_fetch_by_str_path_mut(hash_map, path, &mut |result| { + func(result.ok_or(ReflectPathError::NoItemForIndex { s: path })) + }) + } + None => func(Err(ReflectPathError::NotAnArray)), + }) + } + } + } + } +} + +impl ResolvePath for dyn Reflect { + fn resolve_path<'p>( + &self, + path: &'p str, + func: &mut dyn FnMut(Result<&dyn Reflect, ReflectPathError<'p>>), + ) { + match Component::next(path) { + Ok((component, r)) => component.resolve(self, &mut |result| match result { + Ok(child) => { + if r.is_empty() { + func(Ok(child)) + } else { + child.resolve_path(r, func) + } + } + Err(err) => func(Err(err)), + }), + Err(err) => func(Err(err)), + } + } + + fn resolve_path_mut<'p>( + &mut self, + path: &'p str, + func: &mut dyn FnMut(Result<&mut dyn Reflect, ReflectPathError<'p>>), + ) { + match Component::next(path) { + Ok((component, r)) => component.resolve_mut(self, &mut |result| match result { + Ok(child) => { + if r.is_empty() { + func(Ok(child)) + } else { + child.resolve_path_mut(r, func) + } + } + Err(err) => func(Err(err)), + }), + Err(err) => func(Err(err)), + } + } +} + pub fn is_path_to_array_element(path: &str) -> bool { path.ends_with(']') }