use std::collections::{hash_map::Entry, HashMap};
use scoped_tls_hkt::scoped_thread_local;
use serde::{Deserialize, Serialize, Serializer};
use thiserror::Error;
use uuid::Uuid;
use crate::{internals::entity::EntityHasher, world::Allocate, Entity};
pub trait EntitySerializer: 'static {
fn serialize(&self, entity: Entity, serialize_fn: &mut dyn FnMut(&dyn erased_serde::Serialize));
fn deserialize(
&self,
deserializer: &mut dyn erased_serde::Deserializer,
) -> Result<Entity, erased_serde::Error>;
}
pub trait CustomEntitySerializer {
type SerializedID: serde::Serialize + for<'a> serde::Deserialize<'a>;
fn to_serialized(&self, entity: Entity) -> Self::SerializedID;
fn from_serialized(&self, serialized: Self::SerializedID) -> Entity;
}
impl<T> EntitySerializer for T
where
T: CustomEntitySerializer + 'static,
{
fn serialize(
&self,
entity: Entity,
serialize_fn: &mut dyn FnMut(&dyn erased_serde::Serialize),
) {
let serialized = self.to_serialized(entity);
serialize_fn(&serialized);
}
fn deserialize(
&self,
deserializer: &mut dyn erased_serde::Deserializer,
) -> Result<Entity, erased_serde::Error> {
let serialized =
<<Self as CustomEntitySerializer>::SerializedID as serde::Deserialize>::deserialize(
deserializer,
)?;
Ok(self.from_serialized(serialized))
}
}
scoped_thread_local! {
static ENTITY_SERIALIZER: dyn EntitySerializer
}
pub fn set_entity_serializer<F, R>(entity_serializer: &dyn EntitySerializer, func: F) -> R
where
F: FnOnce() -> R,
{
ENTITY_SERIALIZER.set(entity_serializer, func)
}
impl Serialize for Entity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
ENTITY_SERIALIZER.with(|entity_serializer| {
let mut result = None;
let mut serializer = Some(serializer);
let result_ref = &mut result;
entity_serializer.serialize(*self, &mut move |serializable| {
*result_ref = Some(erased_serde::serialize(
serializable,
serializer
.take()
.expect("serialize can only be called once"),
));
});
result.unwrap()
})
}
}
impl<'de> Deserialize<'de> for Entity {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
ENTITY_SERIALIZER.with(|entity_serializer| {
let mut deserializer = erased_serde::Deserializer::erase(deserializer);
entity_serializer
.deserialize(&mut deserializer)
.map_err(D::Error::custom)
})
}
}
pub type EntityName = [u8; 16];
#[derive(Error, Debug, Copy, Clone, PartialEq, Hash)]
pub enum CanonizeError {
#[error("the entity is already bound to name {0:?}")]
EntityAlreadyBound(Entity, EntityName),
#[error("the name is already bound to a different entity")]
NameAlreadyBound(Entity, EntityName),
}
#[derive(Default, Debug)]
pub struct Canon {
inner: parking_lot::RwLock<CanonInner>,
}
#[derive(Default, Debug)]
struct CanonInner {
to_name: HashMap<Entity, EntityName, EntityHasher>,
to_id: HashMap<EntityName, Entity>,
allocator: Allocate,
}
impl Canon {
pub fn get_id(&self, name: &EntityName) -> Option<Entity> {
self.inner.read().to_id.get(name).copied()
}
pub fn get_name(&self, entity: Entity) -> Option<EntityName> {
self.inner.read().to_name.get(&entity).copied()
}
pub fn canonize_name(&self, name: &EntityName) -> Entity {
let mut inner = self.inner.write();
let inner = &mut *inner;
match inner.to_id.entry(*name) {
Entry::Occupied(occupied) => *occupied.get(),
Entry::Vacant(vacant) => {
let entity = inner.allocator.next().unwrap();
vacant.insert(entity);
inner.to_name.insert(entity, *name);
entity
}
}
}
pub fn canonize_id(&self, entity: Entity) -> EntityName {
let mut inner = self.inner.write();
match inner.to_name.entry(entity) {
Entry::Occupied(occupied) => *occupied.get(),
Entry::Vacant(vacant) => {
let uuid = Uuid::new_v4();
let name = *uuid.as_bytes();
vacant.insert(name);
inner.to_id.insert(name, entity);
name
}
}
}
pub fn canonize(&self, entity: Entity, name: EntityName) -> Result<(), CanonizeError> {
let mut inner = self.inner.write();
if let Some(existing) = inner.to_id.get(&name) {
if existing != &entity {
return Err(CanonizeError::NameAlreadyBound(*existing, name));
}
}
match inner.to_name.entry(entity) {
Entry::Occupied(occupied) => {
if occupied.get() == &name {
Ok(())
} else {
Err(CanonizeError::EntityAlreadyBound(entity, *occupied.get()))
}
}
Entry::Vacant(vacant) => {
vacant.insert(name);
inner.to_id.insert(name, entity);
Ok(())
}
}
}
}
impl CustomEntitySerializer for Canon {
type SerializedID = uuid::Uuid;
fn to_serialized(&self, entity: Entity) -> Self::SerializedID {
uuid::Uuid::from_bytes(self.canonize_id(entity))
}
fn from_serialized(&self, serialized: Self::SerializedID) -> Entity {
self.canonize_name(serialized.as_bytes())
}
}