use std::any::{Any, TypeId};
use std::collections::HashMap;
use glam::{Vec2, Vec3};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeserializeError {
WrongType { expected: &'static str, got: &'static str },
MissingKey(String),
IndexOutOfBounds { index: usize, len: usize },
ParseError(String),
Custom(String),
}
impl std::fmt::Display for DeserializeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeserializeError::WrongType { expected, got } => {
write!(f, "type mismatch: expected {expected}, got {got}")
}
DeserializeError::MissingKey(k) => write!(f, "missing key: '{k}'"),
DeserializeError::IndexOutOfBounds { index, len } => {
write!(f, "index {index} out of bounds (len {len})")
}
DeserializeError::ParseError(s) => write!(f, "parse error: {s}"),
DeserializeError::Custom(s) => write!(f, "{s}"),
}
}
}
impl std::error::Error for DeserializeError {}
#[derive(Debug, Clone, PartialEq)]
pub enum SerializedValue {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Bytes(Vec<u8>),
List(Vec<SerializedValue>),
Map(HashMap<String, SerializedValue>),
}
impl SerializedValue {
pub fn null() -> Self { SerializedValue::Null }
pub fn bool(b: bool) -> Self { SerializedValue::Bool(b) }
pub fn int(i: i64) -> Self { SerializedValue::Int(i) }
pub fn float(f: f64) -> Self { SerializedValue::Float(f) }
pub fn str(s: impl Into<String>) -> Self { SerializedValue::Str(s.into()) }
pub fn bytes(b: Vec<u8>) -> Self { SerializedValue::Bytes(b) }
pub fn list(v: Vec<SerializedValue>) -> Self { SerializedValue::List(v) }
pub fn map(m: HashMap<String, SerializedValue>) -> Self { SerializedValue::Map(m) }
pub fn empty_map() -> Self {
SerializedValue::Map(HashMap::new())
}
pub fn empty_list() -> Self {
SerializedValue::List(Vec::new())
}
pub fn as_bool(&self) -> Option<bool> {
if let SerializedValue::Bool(b) = self { Some(*b) } else { None }
}
pub fn as_int(&self) -> Option<i64> {
match self {
SerializedValue::Int(i) => Some(*i),
SerializedValue::Float(f) => Some(*f as i64),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
SerializedValue::Float(f) => Some(*f),
SerializedValue::Int(i) => Some(*i as f64),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
if let SerializedValue::Str(s) = self { Some(s.as_str()) } else { None }
}
pub fn as_bytes(&self) -> Option<&[u8]> {
if let SerializedValue::Bytes(b) = self { Some(b.as_slice()) } else { None }
}
pub fn as_list(&self) -> Option<&[SerializedValue]> {
if let SerializedValue::List(v) = self { Some(v.as_slice()) } else { None }
}
pub fn as_map(&self) -> Option<&HashMap<String, SerializedValue>> {
if let SerializedValue::Map(m) = self { Some(m) } else { None }
}
pub fn as_map_mut(&mut self) -> Option<&mut HashMap<String, SerializedValue>> {
if let SerializedValue::Map(m) = self { Some(m) } else { None }
}
pub fn is_null(&self) -> bool {
matches!(self, SerializedValue::Null)
}
pub fn get(&self, key: &str) -> Option<&SerializedValue> {
self.as_map()?.get(key)
}
pub fn index(&self, i: usize) -> Option<&SerializedValue> {
self.as_list()?.get(i)
}
pub fn insert(&mut self, key: impl Into<String>, value: SerializedValue) -> bool {
if let SerializedValue::Map(m) = self {
m.insert(key.into(), value);
true
} else {
false
}
}
pub fn push(&mut self, value: SerializedValue) -> bool {
if let SerializedValue::List(v) = self {
v.push(value);
true
} else {
false
}
}
pub fn type_name(&self) -> &'static str {
match self {
SerializedValue::Null => "Null",
SerializedValue::Bool(_) => "Bool",
SerializedValue::Int(_) => "Int",
SerializedValue::Float(_) => "Float",
SerializedValue::Str(_) => "Str",
SerializedValue::Bytes(_) => "Bytes",
SerializedValue::List(_) => "List",
SerializedValue::Map(_) => "Map",
}
}
pub fn to_json_string(&self) -> String {
serde_json::to_string(&self.to_json_value()).expect("a JSON value always serializes")
}
pub fn to_json_value(&self) -> serde_json::Value {
use serde_json::Value as J;
match self {
SerializedValue::Null => J::Null,
SerializedValue::Bool(b) => J::Bool(*b),
SerializedValue::Int(i) => J::from(*i),
SerializedValue::Float(f) => {
let f = if f.is_infinite() { f.signum() * 1e308 } else { *f };
serde_json::Number::from_f64(f).map(J::Number).unwrap_or(J::Null)
}
SerializedValue::Str(s) => J::String(s.clone()),
SerializedValue::Bytes(bytes) => {
use std::fmt::Write;
let mut hex = String::with_capacity(bytes.len() * 2);
for b in bytes {
let _ = write!(hex, "{b:02x}");
}
J::String(hex)
}
SerializedValue::List(items) => J::Array(items.iter().map(Self::to_json_value).collect()),
SerializedValue::Map(m) => {
let mut keys: Vec<&String> = m.keys().collect();
keys.sort();
J::Object(keys.into_iter().map(|k| (k.clone(), m[k].to_json_value())).collect())
}
}
}
pub fn from_json_str(s: &str) -> Result<Self, DeserializeError> {
let v: serde_json::Value =
serde_json::from_str(s).map_err(|e| DeserializeError::ParseError(e.to_string()))?;
Ok(Self::from_json_value(v))
}
pub fn from_json_value(v: serde_json::Value) -> Self {
use serde_json::Value as J;
match v {
J::Null => SerializedValue::Null,
J::Bool(b) => SerializedValue::Bool(b),
J::Number(n) => match n.as_i64() {
Some(i) => SerializedValue::Int(i),
None => SerializedValue::Float(n.as_f64().unwrap_or(f64::NAN)),
},
J::String(s) => SerializedValue::Str(s),
J::Array(items) => SerializedValue::List(items.into_iter().map(Self::from_json_value).collect()),
J::Object(m) => SerializedValue::Map(m.into_iter().map(|(k, v)| (k, Self::from_json_value(v))).collect()),
}
}
}
impl Default for SerializedValue {
fn default() -> Self {
SerializedValue::Null
}
}
pub trait Serialize {
fn serialize(&self) -> SerializedValue;
}
pub trait Deserialize: Sized {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError>;
}
impl Serialize for bool {
fn serialize(&self) -> SerializedValue { SerializedValue::Bool(*self) }
}
impl Deserialize for bool {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_bool().ok_or(DeserializeError::WrongType { expected: "Bool", got: v.type_name() })
}
}
impl Serialize for i32 {
fn serialize(&self) -> SerializedValue { SerializedValue::Int(*self as i64) }
}
impl Deserialize for i32 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_int().map(|i| i as i32)
.ok_or(DeserializeError::WrongType { expected: "Int", got: v.type_name() })
}
}
impl Serialize for i64 {
fn serialize(&self) -> SerializedValue { SerializedValue::Int(*self) }
}
impl Deserialize for i64 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_int().ok_or(DeserializeError::WrongType { expected: "Int", got: v.type_name() })
}
}
impl Serialize for u32 {
fn serialize(&self) -> SerializedValue { SerializedValue::Int(*self as i64) }
}
impl Deserialize for u32 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_int().map(|i| i as u32)
.ok_or(DeserializeError::WrongType { expected: "Int", got: v.type_name() })
}
}
impl Serialize for u64 {
fn serialize(&self) -> SerializedValue { SerializedValue::Int(*self as i64) }
}
impl Deserialize for u64 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_int().map(|i| i as u64)
.ok_or(DeserializeError::WrongType { expected: "Int", got: v.type_name() })
}
}
impl Serialize for f32 {
fn serialize(&self) -> SerializedValue { SerializedValue::Float(*self as f64) }
}
impl Deserialize for f32 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_float().map(|f| f as f32)
.ok_or(DeserializeError::WrongType { expected: "Float", got: v.type_name() })
}
}
impl Serialize for f64 {
fn serialize(&self) -> SerializedValue { SerializedValue::Float(*self) }
}
impl Deserialize for f64 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_float().ok_or(DeserializeError::WrongType { expected: "Float", got: v.type_name() })
}
}
impl Serialize for String {
fn serialize(&self) -> SerializedValue { SerializedValue::Str(self.clone()) }
}
impl Deserialize for String {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
v.as_str().map(|s| s.to_string())
.ok_or(DeserializeError::WrongType { expected: "Str", got: v.type_name() })
}
}
impl Serialize for &str {
fn serialize(&self) -> SerializedValue { SerializedValue::Str(self.to_string()) }
}
impl<T: Serialize> Serialize for Vec<T> {
fn serialize(&self) -> SerializedValue {
SerializedValue::List(self.iter().map(|v| v.serialize()).collect())
}
}
impl<T: Deserialize> Deserialize for Vec<T> {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
let list = v.as_list()
.ok_or(DeserializeError::WrongType { expected: "List", got: v.type_name() })?;
list.iter().map(T::deserialize).collect()
}
}
impl<V: Serialize> Serialize for HashMap<String, V> {
fn serialize(&self) -> SerializedValue {
let mut m = HashMap::new();
for (k, v) in self {
m.insert(k.clone(), v.serialize());
}
SerializedValue::Map(m)
}
}
impl<V: Deserialize> Deserialize for HashMap<String, V> {
fn deserialize(sv: &SerializedValue) -> Result<Self, DeserializeError> {
let map = sv.as_map()
.ok_or(DeserializeError::WrongType { expected: "Map", got: sv.type_name() })?;
let mut out = HashMap::new();
for (k, v) in map {
out.insert(k.clone(), V::deserialize(v)?);
}
Ok(out)
}
}
impl<T: Serialize> Serialize for Option<T> {
fn serialize(&self) -> SerializedValue {
match self {
Some(v) => v.serialize(),
None => SerializedValue::Null,
}
}
}
impl<T: Deserialize> Deserialize for Option<T> {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
if v.is_null() {
Ok(None)
} else {
T::deserialize(v).map(Some)
}
}
}
impl Serialize for Vec2 {
fn serialize(&self) -> SerializedValue {
let mut m = HashMap::new();
m.insert("x".to_string(), SerializedValue::Float(self.x as f64));
m.insert("y".to_string(), SerializedValue::Float(self.y as f64));
SerializedValue::Map(m)
}
}
impl Deserialize for Vec2 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
let x = v.get("x")
.and_then(|v| v.as_float())
.ok_or_else(|| DeserializeError::MissingKey("x".into()))? as f32;
let y = v.get("y")
.and_then(|v| v.as_float())
.ok_or_else(|| DeserializeError::MissingKey("y".into()))? as f32;
Ok(Vec2::new(x, y))
}
}
impl Serialize for Vec3 {
fn serialize(&self) -> SerializedValue {
let mut m = HashMap::new();
m.insert("x".to_string(), SerializedValue::Float(self.x as f64));
m.insert("y".to_string(), SerializedValue::Float(self.y as f64));
m.insert("z".to_string(), SerializedValue::Float(self.z as f64));
SerializedValue::Map(m)
}
}
impl Deserialize for Vec3 {
fn deserialize(v: &SerializedValue) -> Result<Self, DeserializeError> {
let x = v.get("x").and_then(|v| v.as_float())
.ok_or_else(|| DeserializeError::MissingKey("x".into()))? as f32;
let y = v.get("y").and_then(|v| v.as_float())
.ok_or_else(|| DeserializeError::MissingKey("y".into()))? as f32;
let z = v.get("z").and_then(|v| v.as_float())
.ok_or_else(|| DeserializeError::MissingKey("z".into()))? as f32;
Ok(Vec3::new(x, y, z))
}
}
type SerializeFn = Box<dyn Fn(*const u8) -> SerializedValue + Send + Sync>;
type DeserializeFn = Box<dyn Fn(&SerializedValue) -> Box<dyn Any + Send + Sync> + Send + Sync>;
struct ComponentEntry {
type_id: TypeId,
name: String,
serialize: SerializeFn,
deserialize: DeserializeFn,
}
pub struct ComponentSerializer {
by_name: HashMap<String, usize>,
by_type: HashMap<TypeId, usize>,
entries: Vec<ComponentEntry>,
}
impl ComponentSerializer {
pub fn new() -> Self {
Self {
by_name: HashMap::new(),
by_type: HashMap::new(),
entries: Vec::new(),
}
}
pub fn register<T: Serialize + Deserialize + Any + Send + Sync + 'static>(
&mut self,
name: impl Into<String>,
) {
let name = name.into();
let type_id = TypeId::of::<T>();
let idx = self.entries.len();
self.by_name.insert(name.clone(), idx);
self.by_type.insert(type_id, idx);
self.entries.push(ComponentEntry {
type_id,
name,
serialize: Box::new(|ptr| {
let reference = unsafe { &*(ptr as *const T) };
reference.serialize()
}),
deserialize: Box::new(|v| {
match T::deserialize(v) {
Ok(t) => Box::new(t) as Box<dyn Any + Send + Sync>,
Err(_) => Box::new(()) as Box<dyn Any + Send + Sync>,
}
}),
});
}
pub unsafe fn serialize_any(&self, type_id: TypeId, ptr: *const u8) -> Option<SerializedValue> {
let idx = self.by_type.get(&type_id)?;
Some((self.entries[*idx].serialize)(ptr))
}
pub fn deserialize_any(&self, name: &str, v: &SerializedValue) -> Option<Box<dyn Any + Send + Sync>> {
let idx = self.by_name.get(name)?;
Some((self.entries[*idx].deserialize)(v))
}
pub fn has_name(&self, name: &str) -> bool {
self.by_name.contains_key(name)
}
pub fn registered_names(&self) -> impl Iterator<Item = &str> {
self.by_name.keys().map(String::as_str)
}
pub fn len(&self) -> usize {
self.entries.len()
}
}
impl Default for ComponentSerializer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_primitives() {
assert_eq!(true.serialize(), SerializedValue::Bool(true));
assert_eq!(42i64.serialize(), SerializedValue::Int(42));
assert_eq!(3.14f64.serialize(), SerializedValue::Float(3.14));
assert_eq!("hello".serialize(), SerializedValue::Str("hello".into()));
}
#[test]
fn deserialize_primitives() {
assert_eq!(bool::deserialize(&SerializedValue::Bool(false)).unwrap(), false);
assert_eq!(i64::deserialize(&SerializedValue::Int(7)).unwrap(), 7);
assert_eq!(f64::deserialize(&SerializedValue::Float(1.5)).unwrap(), 1.5);
assert_eq!(String::deserialize(&SerializedValue::Str("hi".into())).unwrap(), "hi");
}
#[test]
fn serialize_vec2_roundtrip() {
let v = Vec2::new(1.0, 2.5);
let sv = v.serialize();
let v2 = Vec2::deserialize(&sv).unwrap();
assert!((v.x - v2.x).abs() < 1e-5);
assert!((v.y - v2.y).abs() < 1e-5);
}
#[test]
fn serialize_vec3_roundtrip() {
let v = Vec3::new(1.0, 2.0, 3.0);
let sv = v.serialize();
let v2 = Vec3::deserialize(&sv).unwrap();
assert!((v - v2).length() < 1e-5);
}
#[test]
fn serialize_vec_of_ints() {
let v: Vec<i64> = vec![10, 20, 30];
let sv = v.serialize();
let v2: Vec<i64> = Vec::deserialize(&sv).unwrap();
assert_eq!(v, v2);
}
#[test]
fn serialize_option_some_none() {
let some: Option<i64> = Some(99);
let none: Option<i64> = None;
assert_eq!(some.serialize(), SerializedValue::Int(99));
assert_eq!(none.serialize(), SerializedValue::Null);
assert_eq!(Option::<i64>::deserialize(&SerializedValue::Null).unwrap(), None);
assert_eq!(Option::<i64>::deserialize(&SerializedValue::Int(5)).unwrap(), Some(5));
}
#[test]
fn json_roundtrip_simple() {
let original = SerializedValue::Map({
let mut m = HashMap::new();
m.insert("name".into(), SerializedValue::Str("Alice".into()));
m.insert("score".into(), SerializedValue::Int(1000));
m.insert("alive".into(), SerializedValue::Bool(true));
m
});
let json = original.to_json_string();
let parsed = SerializedValue::from_json_str(&json).unwrap();
assert_eq!(parsed.get("name").and_then(|v| v.as_str()), Some("Alice"));
assert_eq!(parsed.get("score").and_then(|v| v.as_int()), Some(1000));
assert_eq!(parsed.get("alive").and_then(|v| v.as_bool()), Some(true));
}
#[test]
fn json_roundtrip_nested() {
let sv = SerializedValue::List(vec![
SerializedValue::Int(1),
SerializedValue::Float(2.5),
SerializedValue::Null,
SerializedValue::Bool(true),
]);
let json = sv.to_json_string();
let parsed = SerializedValue::from_json_str(&json).unwrap();
assert_eq!(parsed.index(0).and_then(|v| v.as_int()), Some(1));
assert_eq!(parsed.index(2).map(|v| v.is_null()), Some(true));
}
#[test]
fn json_string_escape() {
let sv = SerializedValue::Str("say \"hello\"\nnewline".into());
let json = sv.to_json_string();
let parsed = SerializedValue::from_json_str(&json).unwrap();
assert_eq!(parsed.as_str(), Some("say \"hello\"\nnewline"));
}
#[test]
fn json_keeps_non_ascii_text_intact() {
let name = "Zoë, 東京, \u{1F525}";
let sv = SerializedValue::Str(name.into());
let parsed = SerializedValue::from_json_str(&sv.to_json_string()).unwrap();
assert_eq!(parsed.as_str(), Some(name));
let parsed = SerializedValue::from_json_str(r#""🔥""#).unwrap();
assert_eq!(parsed.as_str(), Some("\u{1F525}"));
}
#[test]
fn json_output_is_deterministic_and_handles_odd_floats() {
let mut m = HashMap::new();
for k in ["zeta", "alpha", "mid"] {
m.insert(k.to_string(), SerializedValue::Float(1.0));
}
m.insert("nan".into(), SerializedValue::Float(f64::NAN));
m.insert("inf".into(), SerializedValue::Float(f64::NEG_INFINITY));
m.insert("raw".into(), SerializedValue::Bytes(vec![0, 15, 255]));
let json = SerializedValue::Map(m).to_json_string();
assert_eq!(
json,
r#"{"alpha":1.0,"inf":-1e+308,"mid":1.0,"nan":null,"raw":"000fff","zeta":1.0}"#
);
let back = SerializedValue::from_json_str(&json).unwrap();
assert_eq!(back.get("alpha"), Some(&SerializedValue::Float(1.0)));
assert_eq!(SerializedValue::from_json_str("42").unwrap(), SerializedValue::Int(42));
assert_eq!(SerializedValue::from_json_str("1e3").unwrap(), SerializedValue::Float(1000.0));
}
#[test]
fn json_rejects_malformed_input() {
for bad in ["{\"a\":1} trailing", "[1,2", "{\"a\" 1}", "\"unterminated", "nul", ""] {
assert!(
matches!(SerializedValue::from_json_str(bad), Err(DeserializeError::ParseError(_))),
"{bad:?} should not parse"
);
}
}
#[test]
fn component_serializer_register_and_use() {
let mut cs = ComponentSerializer::new();
cs.register::<i64>("health");
assert!(cs.has_name("health"));
assert_eq!(cs.len(), 1);
let sv = SerializedValue::Int(100);
let boxed = cs.deserialize_any("health", &sv).unwrap();
let val = boxed.downcast_ref::<i64>().unwrap();
assert_eq!(*val, 100);
}
#[test]
fn wrong_type_error() {
let sv = SerializedValue::Str("not a bool".into());
let err = bool::deserialize(&sv).unwrap_err();
assert!(matches!(err, DeserializeError::WrongType { .. }));
}
}