bevy_archive 0.3.0

An experimental ECS world snapshot system built on Bevy, featuring structured archetype storage and manifest-based serialization.
Documentation
use std::ptr::NonNull;

use bevy_ecs::prelude::*;
use bevy_ecs::ptr::OwningPtr;

use serde::{Deserialize, Serialize};

use crate::prelude::ArenaBox;
pub type ExportFn = fn(&World, Entity) -> Option<serde_json::Value>;
pub type ImportFn = fn(&serde_json::Value, &mut World, Entity) -> Result<(), String>;
pub type DynBuilderFn =
    for<'a> fn(&serde_json::Value, &'a bumpalo::Bump) -> Result<ArenaBox<'a>, String>;

fn short_type_name<T>() -> &'static str {
    std::any::type_name::<T>()
        .rsplit("::")
        .next()
        .unwrap_or("unknown")
}

#[derive(Clone, Debug)]
pub struct JsonValueCodec {
    pub export: ExportFn,
    pub import: ImportFn,
    pub dyn_ctor: DynBuilderFn,
}

fn export<T>(world: &World, entity: Entity) -> Option<serde_json::Value>
where
    T: Serialize + Component,
{
    world
        .entity(entity)
        .get::<T>()
        .and_then(|t| serde_json::to_value(t).ok())
}

fn import<T>(val: &serde_json::Value, world: &mut World, entity: Entity) -> Result<(), String>
where
    T: for<'a> Deserialize<'a> + Component,
{
    let name = short_type_name::<T>();
    serde_json::from_value::<T>(val.clone())
        .map_err(|e| format!("Deserialization error for {}:{}", name, e))
        .map(|v| {
            world.entity_mut(entity).insert(v);
        })
        .map(|_| ())
}
fn dyn_ctor<'a, T>(val: &serde_json::Value, bump: &'a bumpalo::Bump) -> Result<ArenaBox<'a>, String>
where
    T: Serialize + for<'de> Deserialize<'de> + Component,
{
    let name = short_type_name::<T>();
    let component: T = serde_json::from_value(val.clone())
        .map_err(|e| format!("Deserialization error for {}:{}", name, e))?;
    let ptr = bump.alloc(component) as *mut T;
    Ok(unsafe { ArenaBox::new::<T>(OwningPtr::new(NonNull::new_unchecked(ptr.cast()))) })
}
fn export_wrapper<T, T1>(world: &World, entity: Entity) -> Option<serde_json::Value>
where
    T: Component,
    T1: Serialize + for<'a> From<&'a T>,
{
    world
        .entity(entity)
        .get::<T>()
        .and_then(|t| serde_json::to_value(T1::from(t)).ok())
}

fn import_wrapper<T, T1>(
    val: &serde_json::Value,
    world: &mut World,
    entity: Entity,
) -> Result<(), String>
where
    T: Component + From<T1>,
    T1: for<'a> Deserialize<'a> + for<'a> From<&'a T>,
{
    let name = short_type_name::<T>();
    serde_json::from_value::<T1>(val.clone())
        .map_err(|e| format!("Deserialization error for {}:{}", name, e))
        .map(|v| {
            world.entity_mut(entity).insert(T::from(v));
        })
        .map(|_| ())
}
fn dyn_ctor_wrapper<'a, T, T1>(
    val: &serde_json::Value,
    bump: &'a bumpalo::Bump,
) -> Result<ArenaBox<'a>, String>
where
    T: Component + From<T1>,
    T1: Serialize + for<'de> Deserialize<'de> + for<'b> From<&'b T>,
{
    let name = short_type_name::<T>();
    let component: T1 = serde_json::from_value(val.clone())
        .map_err(|e| format!("Deserialization error for {}:{}", name, e))?;
    let ptr = bump.alloc(T::from(component)) as *mut T;
    Ok(unsafe { ArenaBox::new::<T>(OwningPtr::new(NonNull::new_unchecked(ptr.cast()))) })
}

impl JsonValueCodec {
    pub fn new<T>() -> Self
    where
        T: Serialize + for<'a> Deserialize<'a> + Component,
    {
        Self {
            export: export::<T>,
            import: import::<T>,
            dyn_ctor: dyn_ctor::<T>,
        }
    }

    pub fn new_with<T, T1>() -> Self
    where
        T: Component + From<T1>,
        T1: Serialize + for<'a> Deserialize<'a> + for<'a> From<&'a T>,
    {
        Self {
            export: export_wrapper::<T, T1>,
            import: import_wrapper::<T, T1>,
            dyn_ctor: dyn_ctor_wrapper::<T, T1>,
        }
    }
}

pub struct BincodeCodec;