From e6b655fb25a490a6952ad4373ad37a5f5a106e3f Mon Sep 17 00:00:00 2001 From: EliasPrescott <85904049+EliasPrescott@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:58:49 -0400 Subject: [PATCH] adding reflection for Cow<'static, [T]> (#7454) # Objective - Implementing reflection for Cow<'static, [T]> - Hopefully fixes #7429 ## Solution - Implementing Reflect, Typed, GetTypeRegistration, and FromReflect for Cow<'static, [T]> --- ## Notes I have not used bevy_reflection much yet, so I may not fully understand all the use cases. This is also my first attempt at contributing, so I would appreciate any feedback or recommendations for changes. I tried to add cases for using Cow<'static, str> and Cow<'static, [u8]> to some of the bevy_reflect tests, but I can't guarantee those tests are comprehensive enough. --------- Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> Co-authored-by: Alice Cecile --- crates/bevy_reflect/src/impls/std.rs | 172 +++++++++++++++++++++++++++ crates/bevy_reflect/src/lib.rs | 44 ++++++- 2 files changed, 214 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 9f21e2e3f07ee..b3a1c1d43ad2b 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1219,6 +1219,178 @@ impl FromReflect for Cow<'static, str> { } } +impl PathOnly for [T] where [T]: ToOwned {} + +impl TypePath for [T] +where + [T]: ToOwned, +{ + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("[{}]", ::type_path())) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("[{}]", ::short_type_path())) + } +} + +impl PathOnly for T {} + +impl List for Cow<'static, [T]> { + fn get(&self, index: usize) -> Option<&dyn Reflect> { + self.as_ref().get(index).map(|x| x as &dyn Reflect) + } + + fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { + self.to_mut().get_mut(index).map(|x| x as &mut dyn Reflect) + } + + fn len(&self) -> usize { + self.as_ref().len() + } + + fn iter(&self) -> crate::ListIter { + crate::ListIter::new(self) + } + + fn drain(self: Box) -> Vec> { + // into_owned() is not uneccessary here because it avoids cloning whenever you have a Cow::Owned already + #[allow(clippy::unnecessary_to_owned)] + self.into_owned() + .into_iter() + .map(|value| value.clone_value()) + .collect() + } + + fn insert(&mut self, index: usize, element: Box) { + let value = element.take::().unwrap_or_else(|value| { + T::from_reflect(&*value).unwrap_or_else(|| { + panic!( + "Attempted to insert invalid value of type {}.", + value.type_name() + ) + }) + }); + self.to_mut().insert(index, value); + } + + fn remove(&mut self, index: usize) -> Box { + Box::new(self.to_mut().remove(index)) + } + + fn push(&mut self, value: Box) { + let value = T::take_from_reflect(value).unwrap_or_else(|value| { + panic!( + "Attempted to push invalid value of type {}.", + value.type_name() + ) + }); + self.to_mut().push(value); + } + + fn pop(&mut self) -> Option> { + self.to_mut() + .pop() + .map(|value| Box::new(value) as Box) + } +} + +impl Reflect for Cow<'static, [T]> { + fn type_name(&self) -> &str { + std::any::type_name::() + } + + fn into_any(self: Box) -> Box { + self + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn into_reflect(self: Box) -> Box { + self + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + fn apply(&mut self, value: &dyn Reflect) { + crate::list_apply(self, value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::List(self) + } + + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::List(self) + } + + fn reflect_owned(self: Box) -> ReflectOwned { + ReflectOwned::List(self) + } + + fn clone_value(&self) -> Box { + Box::new(List::clone_dynamic(self)) + } + + fn reflect_hash(&self) -> Option { + crate::list_hash(self) + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + crate::list_partial_eq(self, value) + } + + fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { + Some(::type_info()) + } +} + +impl Typed for Cow<'static, [T]> { + fn type_info() -> &'static TypeInfo { + static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); + CELL.get_or_insert::(|| TypeInfo::List(ListInfo::new::())) + } +} + +impl GetTypeRegistration for Cow<'static, [T]> { + fn get_type_registration() -> TypeRegistration { + TypeRegistration::of::>() + } +} + +impl FromReflect for Cow<'static, [T]> { + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::List(ref_list) = reflect.reflect_ref() { + let mut temp_vec = Vec::with_capacity(ref_list.len()); + for field in ref_list.iter() { + temp_vec.push(T::from_reflect(field)?); + } + temp_vec.try_into().ok() + } else { + None + } + } +} + impl Reflect for &'static Path { fn type_name(&self) -> &str { std::any::type_name::() diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index bda0fd48a5f30..7fc8f162a2c69 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -550,8 +550,12 @@ mod tests { ser::{to_string_pretty, PrettyConfig}, Deserializer, }; - use std::fmt::{Debug, Formatter}; - use std::{any::TypeId, marker::PhantomData}; + use std::{ + any::TypeId, + borrow::Cow, + fmt::{Debug, Formatter}, + marker::PhantomData, + }; use super::prelude::*; use super::*; @@ -1031,6 +1035,8 @@ mod tests { b: Bar, u: usize, t: ([f32; 3], String), + v: Cow<'static, str>, + w: Cow<'static, [u8]>, } let foo = Foo { @@ -1039,6 +1045,8 @@ mod tests { b: Bar { y: 255 }, u: 1111111111111, t: ([3.0, 2.0, 1.0], "Tuple String".to_string()), + v: Cow::Owned("Cow String".to_string()), + w: Cow::Owned(vec![1, 2, 3]), }; let foo2: Box = Box::new(foo.clone()); @@ -1394,6 +1402,38 @@ mod tests { let info = value.get_represented_type_info().unwrap(); assert!(info.is::()); + // Cow<'static, str> + type MyCowStr = Cow<'static, str>; + + let info = MyCowStr::type_info(); + if let TypeInfo::Value(info) = info { + assert!(info.is::()); + assert_eq!(std::any::type_name::(), info.type_name()); + } else { + panic!("Expected `TypeInfo::Value`"); + } + + let value: &dyn Reflect = &Cow::<'static, str>::Owned("Hello!".to_string()); + let info = value.get_represented_type_info().unwrap(); + assert!(info.is::()); + + // Cow<'static, [u8]> + type MyCowSlice = Cow<'static, [u8]>; + + let info = MyCowSlice::type_info(); + if let TypeInfo::List(info) = info { + assert!(info.is::()); + assert!(info.item_is::()); + assert_eq!(std::any::type_name::(), info.type_name()); + assert_eq!(std::any::type_name::(), info.item_type_name()); + } else { + panic!("Expected `TypeInfo::List`"); + } + + let value: &dyn Reflect = &Cow::<'static, [u8]>::Owned(vec![0, 1, 2, 3]); + let info = value.get_represented_type_info().unwrap(); + assert!(info.is::()); + // Map type MyMap = HashMap;