diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..eec93ff --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "sandbox-reflection" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.145", features = ["derive"] } +speedy = "0.8.3" + +[dev-dependencies] +serde_json = "1.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..63cf030 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,375 @@ +use std::{any::Any, collections::HashMap, fmt}; + +use serde::{Deserialize, Serialize}; +use speedy::{Readable, Writable}; + +#[cfg(test)] +mod tests; + +pub trait Reflect: Any + Send + 'static { + fn as_any(&self) -> &dyn Any; + + fn as_any_mut(&mut self) -> &mut dyn Any; + + fn as_reflect(&self) -> &dyn Reflect; + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect; + + fn patch(&mut self, value: &dyn Reflect); + + fn as_struct(&self) -> Option<&dyn Struct> { + None + } + + fn as_struct_mut(&mut self) -> Option<&mut dyn Struct> { + None + } +} + +impl dyn Reflect { + pub fn downcast_ref(&self) -> Option<&T> + where + T: Reflect, + { + self.as_any().downcast_ref::() + } + + pub fn downcast_mut(&mut self) -> Option<&mut T> + where + T: Reflect, + { + self.as_any_mut().downcast_mut::() + } +} + +macro_rules! impl_for_core_types { + ($($ty:ident)*) => { + $( + impl Reflect for $ty { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + fn patch(&mut self, value: &dyn Reflect) { + if let Some(value) = value.as_any().downcast_ref::() { + *self = value.to_owned(); + } + } + } + + impl FromReflect for $ty { + fn from_reflect(reflect: &dyn Reflect) -> Option { + Some(reflect.downcast_ref::<$ty>()?.to_owned()) + } + } + + impl IntoValue for $ty { + fn into_value(self) -> Value { + Value(ValueInner::$ty(self)) + } + } + + impl private::Sealed for $ty {} + )* + }; +} + +impl_for_core_types! { + usize u8 u16 u32 u64 u128 + i8 i16 i32 i64 i128 + f32 f64 + bool char String +} + +pub trait FromReflect: Reflect + Sized { + fn from_reflect(reflect: &dyn Reflect) -> Option; +} + +pub trait Struct: Reflect { + fn field(&self, name: &str) -> Option<&dyn Reflect>; + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect>; + + fn into_value(self) -> StructValue; + + fn fields(&self) -> FieldsIter<'_>; + + fn fields_mut(&mut self) -> FieldsIterMut<'_>; +} + +pub struct FieldsIter<'a> { + iter: Box + 'a>, +} + +impl<'a> FieldsIter<'a> { + pub fn new(iter: I) -> Self + where + I: Iterator + 'a, + { + Self { + iter: Box::new(iter), + } + } +} + +impl<'a> Iterator for FieldsIter<'a> { + type Item = (&'a str, &'a dyn Reflect); + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +pub struct FieldsIterMut<'a> { + iter: Box + 'a>, +} + +impl<'a> FieldsIterMut<'a> { + pub fn new(iter: I) -> Self + where + I: Iterator + 'a, + { + Self { + iter: Box::new(iter), + } + } +} + +impl<'a> Iterator for FieldsIterMut<'a> { + type Item = (&'a str, &'a mut dyn Reflect); + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +#[derive(Readable, Writable, Serialize, Deserialize, Debug, Clone)] +pub struct StructValue { + fields: HashMap, +} + +impl StructValue { + pub fn builder() -> StructValueBuilder { + StructValueBuilder::default() + } +} + +impl Reflect for StructValue { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + fn patch(&mut self, value: &dyn Reflect) { + if let Some(struct_) = value.as_struct() { + for (name, value) in &mut self.fields { + if let Some(new_value) = struct_.field(name) { + value.patch(new_value); + } + } + } + } + + fn as_struct(&self) -> Option<&dyn Struct> { + Some(self) + } + + fn as_struct_mut(&mut self) -> Option<&mut dyn Struct> { + Some(self) + } +} + +impl Struct for StructValue { + fn field(&self, name: &str) -> Option<&dyn Reflect> { + Some(self.fields.get(name)?) + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { + Some(self.fields.get_mut(name)?) + } + + fn into_value(self) -> StructValue { + self + } + + fn fields(&self) -> FieldsIter<'_> { + let iter = self + .fields + .iter() + .map(|(key, value)| (&**key, value.as_reflect())); + FieldsIter::new(iter) + } + + fn fields_mut(&mut self) -> FieldsIterMut<'_> { + let iter = self + .fields + .iter_mut() + .map(|(key, value)| (&**key, value.as_reflect_mut())); + FieldsIterMut::new(iter) + } +} + +#[derive(Default)] +pub struct StructValueBuilder { + fields: HashMap, +} + +impl StructValueBuilder { + pub fn set(mut self, name: impl Into, value: impl IntoValue) -> Self { + self.fields.insert(name.into(), value.into_value()); + self + } + + pub fn build(self) -> StructValue { + StructValue { + fields: self.fields, + } + } +} + +#[derive(Readable, Writable, Serialize, Deserialize, Clone)] +pub struct Value(ValueInner); + +impl Reflect for Value { + fn as_any(&self) -> &dyn Any { + self.0.as_any() + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self.0.as_any_mut() + } + + fn as_reflect(&self) -> &dyn Reflect { + self.0.as_reflect() + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self.0.as_reflect_mut() + } + + #[allow(warnings)] + fn patch(&mut self, value: &dyn Reflect) { + self.0.patch(value) + } +} + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +macro_rules! value_inner { + ( + $(#[$m:meta])* + enum ValueInner { + $($ident:ident,)* + } + ) => { + $(#[$m])* + enum ValueInner { + $($ident($ident),)* + } + + impl Reflect for ValueInner { + fn as_any(&self) -> &dyn Any { + match self { + $( + Self::$ident(inner) => inner, + )* + } + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + match self { + $( + Self::$ident(inner) => inner, + )* + } + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + fn patch(&mut self, value: &dyn Reflect) { + match self { + $( + Self::$ident(inner) => { + if let Some(value) = value.downcast_ref::<$ident>() { + *inner = value.to_owned(); + } + }, + )* + } + } + } + }; +} + +value_inner! { + #[allow(non_camel_case_types)] + #[derive(Readable, Writable, Serialize, Deserialize, Debug, Clone)] + enum ValueInner { + usize, + u8, + u16, + u32, + u64, + u128, + i8, + i16, + i32, + i64, + i128, + bool, + char, + f32, + f64, + String, + StructValue, + } +} + +mod private { + pub trait Sealed {} +} + +pub trait IntoValue: private::Sealed { + fn into_value(self) -> Value; +} + +impl IntoValue for StructValue { + fn into_value(self) -> Value { + Value(ValueInner::StructValue(self)) + } +} + +impl private::Sealed for StructValue {} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..7a10e24 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,161 @@ +use std::any::Any; + +use crate::{FieldsIter, FieldsIterMut, FromReflect, Reflect, Struct, StructValue}; + +#[derive(Default)] +struct Foo { + field: i32, +} + +impl Reflect for Foo { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + fn patch(&mut self, value: &dyn Reflect) { + if let Some(value) = value.as_struct() { + if let Some(field) = value.field("field") { + self.field_mut("field").unwrap().patch(field); + } + } + } + + fn as_struct(&self) -> Option<&dyn Struct> { + Some(self) + } + + fn as_struct_mut(&mut self) -> Option<&mut dyn Struct> { + Some(self) + } +} + +impl FromReflect for Foo { + fn from_reflect(reflect: &dyn Reflect) -> Option { + let struct_ = reflect.as_struct()?; + Some(Self { + field: struct_.field("field")?.downcast_ref::()?.to_owned(), + }) + } +} + +impl Struct for Foo { + fn field(&self, name: &str) -> Option<&dyn Reflect> { + if name == "field" { + return Some(&self.field); + } + + None + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { + if name == "field" { + return Some(&mut self.field); + } + + None + } + + fn into_value(self) -> StructValue { + StructValue::builder().set("field", self.field).build() + } + + fn fields(&self) -> FieldsIter<'_> { + let iter = std::iter::once(("field", self.field.as_reflect())); + FieldsIter::new(iter) + } + + fn fields_mut(&mut self) -> FieldsIterMut<'_> { + let iter = std::iter::once(("field", self.field.as_reflect_mut())); + FieldsIterMut::new(iter) + } +} + +#[test] +fn accessing_fields() { + let foo = Foo { field: 42 }; + let struct_: &dyn Struct = foo.as_struct().unwrap(); + + let value = struct_ + .field("field") + .unwrap() + .downcast_ref::() + .unwrap(); + + assert_eq!(*value, 42); +} + +#[test] +fn patching() { + let mut foo = Foo { field: 42 }; + + let patch = StructValue::builder().set("field", 1337).build(); + + foo.patch(&patch); + + assert_eq!(foo.field, 1337); +} + +#[test] +fn patching_struct_value() { + let mut value = StructValue::builder().set("field", 42).build(); + let patch = StructValue::builder().set("field", 1337).build(); + value.patch(&patch); + + assert_eq!( + value.field("field").unwrap().downcast_ref::().unwrap(), + &1337 + ); +} + +// #[test] +// fn patching_value() { +// todo!() +// } + +#[test] +fn from_reflect() { + let foo = Foo::default(); + let foo_reflect: &dyn Reflect = &foo; + + let foo = Foo::from_reflect(foo_reflect).unwrap(); + + assert_eq!(foo.field, 0); +} + +#[test] +fn serialize_deserialize() { + let foo = Foo::default(); + let struct_value = foo.into_value(); + + let json = serde_json::to_string(&struct_value).unwrap(); + + let struct_value = serde_json::from_str::(&json).unwrap(); + let foo = Foo::from_reflect(&struct_value).unwrap(); + + assert_eq!(foo.field, 0); +} + +#[test] +fn fields() { + let foo = Foo::default(); + + for (name, value) in foo.fields() { + if name == "field" { + assert_eq!(foo.field, i32::from_reflect(value).unwrap()); + } else { + panic!("Unknown field {name:?}"); + } + } +}