continue splitting reflect module

This commit is contained in:
Dmitry Stepanov
2026-05-06 11:03:47 +03:00
parent e9b389be77
commit 747fa780f5
4 changed files with 595 additions and 524 deletions
+181
View File
@@ -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<T: Reflect> 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::<T>()
}
}
#[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<f64>,
/// A minimal value of the property. Works only with numeric properties!
pub max_value: Option<f64>,
/// A minimal value of the property. Works only with numeric properties!
pub step: Option<f64>,
/// Maximum amount of decimal places for a numeric property.
pub precision: Option<usize>,
}
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<T: 'static>(&self) -> Result<&T, CastError> {
match self.value.field_value_as_any_ref().downcast_ref::<T>() {
Some(value) => Ok(value),
None => Err(CastError::TypeMismatch {
property_name: self.metadata.name.to_string(),
expected_type_id: TypeId::of::<T>(),
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<Self> 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<Self> 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)
}
}
+34
View File
@@ -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;
}
+59
View File
@@ -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<Option<Box<dyn Reflect>>, 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<dyn Reflect>;
/// 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;
}
+321 -524
View File
@@ -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<T: Reflect> 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::<T>()
}
}
#[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<f64>,
/// A minimal value of the property. Works only with numeric properties!
pub max_value: Option<f64>,
/// A minimal value of the property. Works only with numeric properties!
pub step: Option<f64>,
/// Maximum amount of decimal places for a numeric property.
pub precision: Option<usize>,
}
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<T: 'static>(&self) -> Result<&T, CastError> {
match self.value.field_value_as_any_ref().downcast_ref::<T>() {
Some(value) => Ok(value),
None => Err(CastError::TypeMismatch {
property_name: self.metadata.name.to_string(),
expected_type_id: TypeId::of::<T>(),
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<Self> 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<Self> 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<Option<Box<dyn Reflect>>, 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<dyn Reflect>;
/// 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<T: Reflect> 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<Component> {
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<T: 'static>(&self, name: &str, func: &mut dyn FnMut(Option<&T>));
fn get_field_mut<T: 'static>(&mut self, _name: &str, func: &mut dyn FnMut(Option<&mut T>));
}
impl<R: Reflect> GetField for R {
fn get_field<T: 'static>(&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<T: 'static>(&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<String> {
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::<usize>() {
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::<usize>() {
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<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
@@ -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<T: Reflect> 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<Component> {
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<T: 'static>(&self, name: &str, func: &mut dyn FnMut(Option<&T>));
fn get_field_mut<T: 'static>(&mut self, _name: &str, func: &mut dyn FnMut(Option<&mut T>));
}
impl<R: Reflect> GetField for R {
fn get_field<T: 'static>(&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<T: 'static>(&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<String> {
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::<usize>() {
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::<usize>() {
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(']')
}