use alloc::string::ToString;
use core::{any::Any, mem::transmute};
use crate::*;
fn demote_static<'db>(ty: StaticType) -> DynamicType<'db> {
unsafe { transmute::<DynamicType<'static>, DynamicType<'db>>(ty) }
}
#[derive(Clone, Default)]
pub struct Db<'db> {
pub(crate) known_types: BTreeMap<TypeId, (DynamicType<'db>, FieldsStorage<'db>)>,
#[cfg(feature = "use_serde")]
pub(crate) deserialize_trampolines: BTreeMap<TypeId, DeserializeTrampoline>,
#[cfg(feature = "use_serde")]
pub(crate) serialize_vtables: BTreeMap<TypeId, *mut ()>,
}
#[derive(Clone)]
pub struct FieldsStorage<'db> {
pub(crate) area: Cow<'db, [u8]>,
}
impl<'db> FieldsStorage<'db> {
pub fn borrow(&'db self) -> Self {
Self::from_inner(Cow::Borrowed(&self.area))
}
pub fn from_inner(area: Cow<'db, [u8]>) -> FieldsStorage {
FieldsStorage { area }
}
pub fn into_inner(self) -> Cow<'db, [u8]> {
self.area
}
pub fn cursor(&'db self) -> FieldsCursor<'db> {
FieldsCursor {
cursor: Cell::new(self.area.as_ptr() as *mut u8),
_marker: Default::default(),
}
}
}
impl<'db> Db<'db> {
pub fn new() -> Self {
Default::default()
}
#[cfg(feature = "use_serde")]
pub fn deserialize<'de, T: Any, D>(
&'db self,
src: D,
) -> Result<T, <D as serde::Deserializer<'de>>::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::DeserializeSeed;
let (rust_type, fields) = self
.known_types
.get(&TypeId::of::<T>())
.ok_or_else(|| serde::de::Error::custom("type missing from db, cannot deserialize"))?;
let mut uninit = core::mem::MaybeUninit::uninit();
crate::Deserialize {
db: self,
name: rust_type.name,
dst: ShapedOutputLocation {
ptr: uninit.as_mut_ptr() as *mut u8,
shape: &rust_type.shape,
fields: &fields.cursor(),
_data: Default::default(),
},
}
.deserialize(src)?;
Ok(unsafe { uninit.assume_init() })
}
pub fn serialize<S: serde::Serializer, T: HasKey>(
&'db self,
s: S,
val: &T,
) -> Result<S::Ok, S::Error> {
let id = TypeId::of::<T::Key>();
let (rust_type, fields) = self.known_types.get(&id).ok_or_else(|| {
<S::Error as serde::ser::Error>::custom("type missing from db, cannot serialize")
})?;
<Serialize as serde::Serialize>::serialize(
&Serialize::new(
self,
rust_type.name,
&fields.cursor(),
&ShapedLocation {
shape: &rust_type.shape,
ptr: val as *const _ as *const _,
fields: FieldsStorage::from_inner(Cow::Borrowed(&fields.area)),
_data: Default::default(),
},
),
s,
)
}
pub fn register_type<T: Reflect>(&mut self, fields: FieldsStorage<'db>) -> &mut Db<'db> {
self.insert(
TypeId::of::<T::Key>(),
demote_static(T::rust_type()),
fields,
);
self
}
pub fn register_const<T: StaticReflect>(&mut self) -> &mut Db<'db> {
self.insert(
TypeId::of::<T::Key>(),
demote_static(Cow::Borrowed(&T::RUST_TYPE)),
T::FIELDS.clone(),
);
self
}
pub fn insert(&mut self, id: TypeId, val: DynamicType<'db>, fields: FieldsStorage<'db>) {
self.known_types.insert(id, (val, fields));
}
#[cfg(feature = "use_serde")]
pub fn register_serde_leaf<
T: HasKey + Default + serde::Serialize + for<'de> serde::Deserialize<'de>,
>(
&mut self,
) -> &mut Db<'db> {
fn de<T: for<'b> serde::Deserialize<'b>>(
d: &mut dyn erased_serde::Deserializer,
dst: &ShapedOutputLocation,
) -> Result<(), erased_serde::Error> {
let x: T = T::deserialize(d)?;
unsafe { dst.ptr.cast::<T>().write(x) };
Ok(())
}
let typeid = TypeId::of::<T::Key>();
let vtable = unsafe {
core::mem::transmute::<&dyn erased_serde::Serialize, TraitObject>(
&T::default() as &dyn erased_serde::Serialize
)
.vtable
};
self.serialize_vtables.insert(typeid, vtable);
self.deserialize_trampolines.insert(typeid, de::<T>);
self
}
#[cfg(feature = "use_serde")]
pub(crate) fn deserialize_leaf<'de, 'data, 'visitor, 'fields, D>(
&self,
d: D,
leaf: TypeId,
dst: &ShapedOutputLocation<'data, 'visitor, 'fields>,
) -> Result<(), erased_serde::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error as deError;
let trampoline = self.deserialize_trampolines.get(&leaf).ok_or_else(|| {
erased_serde::Error::custom(alloc::format!(
"leaf type {:?} missing from reflection db",
leaf
))
})?;
trampoline(&mut erased_serde::Deserializer::erase(d), dst)
.map_err(|e| erased_serde::Error::custom(e.to_string()))
}
pub(crate) fn lookup_id(
&'db self,
id: TypeId,
) -> Result<&'db (DynamicType<'db>, FieldsStorage), alloc::string::String> {
self.known_types
.get(&id)
.ok_or_else(|| alloc::format!("reflection db missing type info for typeid {:?}", id))
}
}
#[doc(hidden)]
pub trait HasKey {
type Key: 'static;
}
macro_rules! haskey {
($($name:ident),+) => {
$(
impl<'a> HasKey for $name<'a> {
type Key = $name<'static>;
}
)+
}
}
haskey!(EnumArm, DataShape, Field);