use std::any::Any; use serde::{de::DeserializeOwned, Serialize}; pub use ecsdb_derive::Component; pub trait Component: Sized + Any + ComponentRead + ComponentWrite { type Storage; const NAME: &'static str; fn component_name() -> &'static str { Self::NAME } } pub trait ComponentWrite { fn to_rusqlite(component: C) -> Result; } pub trait ComponentRead { fn from_rusqlite(value: rusqlite::types::Value) -> Result; } impl ComponentRead for C where C: Component, S: ComponentRead, { fn from_rusqlite(value: rusqlite::types::Value) -> Result { S::from_rusqlite(value) } } impl ComponentWrite for C where C: Component, S: ComponentWrite, { fn to_rusqlite(component: Self) -> Result { S::to_rusqlite(component) } } pub struct JsonStorage; #[derive(thiserror::Error, Debug)] #[error("Error storing Component: {0}")] pub struct StorageError(String); impl ComponentRead for JsonStorage where C: Component + DeserializeOwned, { fn from_rusqlite(value: rusqlite::types::Value) -> Result { match value { rusqlite::types::Value::Text(s) => { serde_json::from_str(&s).map_err(|e| StorageError(e.to_string())) } other => Err(StorageError(format!("Unexpected type {other:?}"))), } } } impl ComponentWrite for JsonStorage where C: Component + Serialize, { fn to_rusqlite(component: C) -> Result { let json = serde_json::to_string(&component).map_err(|e| StorageError(e.to_string()))?; Ok(rusqlite::types::Value::Text(json)) } } pub struct BlobStorage; impl ComponentRead for BlobStorage where C: Component + From>, { fn from_rusqlite(value: rusqlite::types::Value) -> Result { match value { rusqlite::types::Value::Blob(b) => Ok(C::from(b)), other => Err(StorageError(format!("Unexpected type {other:?}"))), } } } impl ComponentWrite for BlobStorage where C: Component + Into>, { fn to_rusqlite(component: C) -> Result { Ok(rusqlite::types::Value::Blob(component.into())) } } pub struct NullStorage; impl ComponentRead for NullStorage where C: Component + DeserializeOwned, { fn from_rusqlite(value: rusqlite::types::Value) -> Result { match value { rusqlite::types::Value::Null => { serde_json::from_str("null").map_err(|e| StorageError(e.to_string())) } other => Err(StorageError(format!("Unexpected type {other:?}"))), } } } impl ComponentWrite for NullStorage where C: Component + Serialize, { fn to_rusqlite(_component: C) -> Result { Ok(rusqlite::types::Value::Null) } } pub trait Bundle { const COMPONENTS: &'static [&'static str]; fn component_names() -> &'static [&'static str] { Self::COMPONENTS } fn to_rusqlite(self) -> Result, StorageError>; } impl Bundle for C { const COMPONENTS: &'static [&'static str] = &[C::NAME]; fn to_rusqlite(self) -> Result, StorageError> { Ok(vec![(C::NAME, C::to_rusqlite(self)?)]) } } macro_rules! bundle_tuple_impls { ($t:tt, $($ts:tt),+) => { impl<$t, $($ts,)+> Bundle for ($t, $($ts,)+) where $t: Component, $($ts: Component,)+ { const COMPONENTS: &'static [&'static str] = &[ $t::NAME, $($ts::NAME,)+ ]; fn to_rusqlite( self ) -> Result, StorageError> { #[allow(non_snake_case)] let ($t, $($ts,)+) = self; Ok( vec![ ($t::NAME, $t::to_rusqlite($t)?), $(($ts::NAME, $ts::to_rusqlite($ts)?),)+ ] ) } } bundle_tuple_impls!($($ts),+); }; ($t:tt) => { impl<$t: Component> Bundle for ($t,) { const COMPONENTS: &'static [&'static str] = &[ $t::NAME ]; fn to_rusqlite( self ) -> Result, StorageError> { let (t,) = self; Ok( vec![($t::NAME, $t::to_rusqlite(t)?)] ) } } }; } bundle_tuple_impls!(A, B, C);