bevy_archive 0.3.0

An experimental ECS world snapshot system built on Bevy, featuring structured archetype storage and manifest-based serialization.
Documentation
use bevy_ecs::{component::ComponentId, prelude::*};

use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};

use crate::prelude::codec::JsonValueCodec;
#[cfg(feature = "arrow_rs")]
use crate::prelude::vec_snapshot_factory::ArrowSnapshotFactory;
pub mod codec;

pub type CompIdFn = fn(&World) -> Option<ComponentId>;
pub type CompRegFn = fn(&mut World) -> ComponentId;

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

#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum SnapshotMode {
    #[default]
    Full,
    EmplaceIfNotExists,
}

#[derive(Clone, Debug)]
pub struct SnapshotFactory {
    pub js_value: JsonValueCodec,
    #[cfg(feature = "arrow_rs")]
    pub arrow: Option<ArrowSnapshotFactory>,
    pub comp_id: CompIdFn,
    pub register: CompRegFn,
    pub mode: SnapshotMode,
}

#[cfg(feature = "arrow_rs")]
macro_rules! arrow_ext {
    ($text:ty) => {
        $text
    };
}

#[cfg(not(feature = "arrow_rs"))]
macro_rules! arrow_ext {
    ($text:ty) => {
        ()
    };
}

macro_rules! feature_expr {
    ($feature:literal, $expr:expr) => {{
        #[cfg(feature = $feature)]
        {
            $expr
        }
        #[cfg(not(feature = $feature))]
        {
            ()
        }
    }};
}
type SnapshotTuple = (JsonValueCodec, arrow_ext!(Option<ArrowSnapshotFactory>));
impl SnapshotFactory {
    #[inline]
    #[allow(unused_variables)]
    fn from_mode_tuple(
        mode: SnapshotMode,
        comp_id: CompIdFn,
        register: CompRegFn,
        parts: SnapshotTuple,
    ) -> Self {
        let (js_value, arrow) = parts;
        SnapshotFactory {
            js_value,
            #[cfg(feature = "arrow_rs")]
            arrow,
            mode,
            comp_id,
            register,
        }
    }
}

macro_rules! build_common {
    ($t:ty ) => {
        (SnapshotFactory::component_id::<$t>, |world| {
            world.register_component::<$t>()
        })
    };
}

impl SnapshotFactory {
    #[inline]
    fn component_id<T: Component>(world: &World) -> Option<ComponentId> {
        world.component_id::<T>()
    }

    pub fn new<T>(mode: SnapshotMode) -> Self
    where
        T: Serialize + DeserializeOwned + Component + 'static,
    {
        let (comp_id, register): (CompIdFn, CompRegFn) = build_common!(T);
        let js = JsonValueCodec::new::<T>();
        let arrow = feature_expr!("arrow_rs", Some(ArrowSnapshotFactory::new::<T>()));
        SnapshotFactory::from_mode_tuple(mode, comp_id, register, (js, arrow))
    }
    pub fn new_with_wrapper<T, T1>(mode: SnapshotMode) -> Self
    where
        T: Component + From<T1>,
        T1: Serialize + DeserializeOwned + for<'a> From<&'a T>,
    {
        let (comp_id, register): (CompIdFn, CompRegFn) = build_common!(T);

        let js = JsonValueCodec::new_with::<T, T1>();
        let arrow = feature_expr!("arrow_rs", Some(ArrowSnapshotFactory::new_with::<T, T1>()));
        return SnapshotFactory::from_mode_tuple(mode, comp_id, register, (js, arrow));
    }
}