use std::{
any::Any,
fmt::Debug,
sync::{Arc, OnceLock},
};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::{Value, value::RawValue};
type AMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
use crate::common::with_id::WithId;
pub trait AnyItem: WithId + erased_serde::Serialize + Any + Debug + Send + Sync + 'static {
fn as_any(&self) -> &dyn Any;
fn entity_type(&self) -> &'static str;
fn equals(&self, other: &dyn AnyItem) -> bool;
fn server_owner(&self) -> Option<&str> {
None
}
fn bake_server_owner(&self, server_id: &str) -> Option<Arc<dyn AnyItem>> {
let _ = server_id;
None
}
}
erased_serde::serialize_trait_object!(AnyItem);
impl PartialEq for dyn AnyItem {
fn eq(&self, other: &Self) -> bool {
self.equals(other)
}
}
impl Eq for dyn AnyItem {}
inventory::collect!(ItemRegistration);
pub type ItemParseFn = fn(Value) -> Result<Arc<dyn AnyItem>, anyhow::Error>;
pub type ItemParseBytesFn = fn(&[u8]) -> Result<Arc<dyn AnyItem>, anyhow::Error>;
pub type ItemSerializeJsonFn = fn(&dyn AnyItem) -> Result<Box<RawValue>, serde_json::Error>;
pub struct ItemRegistration {
pub entity_type: &'static str,
pub crate_name: &'static str,
pub parse: ItemParseFn,
pub parse_bytes: ItemParseBytesFn,
pub serialize_json: ItemSerializeJsonFn,
}
fn item_registry_index() -> &'static AMap<&'static str, &'static ItemRegistration> {
static INDEX: OnceLock<AMap<&'static str, &'static ItemRegistration>> = OnceLock::new();
INDEX.get_or_init(|| {
inventory::iter::<ItemRegistration>
.into_iter()
.map(|reg| (reg.entity_type, reg))
.collect()
})
}
pub fn lookup_item_registration(entity_type: &str) -> Option<&'static ItemRegistration> {
item_registry_index().get(entity_type).copied()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum IngestBufferPolicy {
#[default]
None,
TimeWindow {
window_ms: u64,
},
}
pub struct IngestBufferRegistration {
pub entity_type: &'static str,
pub policy: IngestBufferPolicy,
}
inventory::collect!(IngestBufferRegistration);
pub trait Eventable:
AnyItem + Serialize + DeserializeOwned + Clone + PartialEq + Sized + Any
{
const ENTITY_NAME_STATIC: &'static str;
fn entity_name_static() -> &'static str {
Self::ENTITY_NAME_STATIC
}
fn ingest_buffer_policy() -> IngestBufferPolicy {
IngestBufferPolicy::None
}
fn parse(value: Value) -> Result<Arc<dyn AnyItem>, anyhow::Error> {
let item = serde_json::from_value::<Self>(value)?;
Ok(Arc::new(item))
}
fn parse_bytes(bytes: &[u8]) -> Result<Arc<dyn AnyItem>, anyhow::Error> {
let item = serde_json::from_slice::<Self>(bytes)?;
Ok(Arc::new(item))
}
}