Experiments in applying Entity-Component-System patterns to durable data storage APIs.
1use std::any::Any; 2 3use serde::{de::DeserializeOwned, Serialize}; 4 5pub use ecsdb_derive::Component; 6 7pub trait Component: Sized + Any + ComponentRead<Self> + ComponentWrite<Self> { 8 type Storage; 9 10 const NAME: &'static str; 11 fn component_name() -> &'static str { 12 Self::NAME 13 } 14} 15 16pub trait ComponentWrite<C> { 17 fn to_rusqlite(component: C) -> Result<rusqlite::types::Value, StorageError>; 18} 19 20pub trait ComponentRead<C> { 21 fn from_rusqlite(value: rusqlite::types::Value) -> Result<C, StorageError>; 22} 23 24impl<C, S> ComponentRead<Self> for C 25where 26 C: Component<Storage = S>, 27 S: ComponentRead<C>, 28{ 29 fn from_rusqlite(value: rusqlite::types::Value) -> Result<Self, StorageError> { 30 S::from_rusqlite(value) 31 } 32} 33 34impl<C, S> ComponentWrite<Self> for C 35where 36 C: Component<Storage = S>, 37 S: ComponentWrite<C>, 38{ 39 fn to_rusqlite(component: Self) -> Result<rusqlite::types::Value, StorageError> { 40 S::to_rusqlite(component) 41 } 42} 43 44pub struct JsonStorage; 45 46#[derive(thiserror::Error, Debug)] 47#[error("Error storing Component: {0}")] 48pub struct StorageError(String); 49 50impl<C> ComponentRead<C> for JsonStorage 51where 52 C: Component + DeserializeOwned, 53{ 54 fn from_rusqlite(value: rusqlite::types::Value) -> Result<C, StorageError> { 55 match value { 56 rusqlite::types::Value::Text(s) => { 57 serde_json::from_str(&s).map_err(|e| StorageError(e.to_string())) 58 } 59 other => Err(StorageError(format!("Unexpected type {other:?}"))), 60 } 61 } 62} 63 64impl<C> ComponentWrite<C> for JsonStorage 65where 66 C: Component + Serialize, 67{ 68 fn to_rusqlite(component: C) -> Result<rusqlite::types::Value, StorageError> { 69 let json = serde_json::to_string(&component).map_err(|e| StorageError(e.to_string()))?; 70 Ok(rusqlite::types::Value::Text(json)) 71 } 72} 73 74pub struct BlobStorage; 75 76impl<C> ComponentRead<C> for BlobStorage 77where 78 C: Component + From<Vec<u8>>, 79{ 80 fn from_rusqlite(value: rusqlite::types::Value) -> Result<C, StorageError> { 81 match value { 82 rusqlite::types::Value::Blob(b) => Ok(C::from(b)), 83 other => Err(StorageError(format!("Unexpected type {other:?}"))), 84 } 85 } 86} 87 88impl<C> ComponentWrite<C> for BlobStorage 89where 90 C: Component + Into<Vec<u8>>, 91{ 92 fn to_rusqlite(component: C) -> Result<rusqlite::types::Value, StorageError> { 93 Ok(rusqlite::types::Value::Blob(component.into())) 94 } 95} 96 97pub struct NullStorage; 98 99impl<C> ComponentRead<C> for NullStorage 100where 101 C: Component + DeserializeOwned, 102{ 103 fn from_rusqlite(value: rusqlite::types::Value) -> Result<C, StorageError> { 104 match value { 105 rusqlite::types::Value::Null => { 106 serde_json::from_str("null").map_err(|e| StorageError(e.to_string())) 107 } 108 other => Err(StorageError(format!("Unexpected type {other:?}"))), 109 } 110 } 111} 112 113impl<C> ComponentWrite<C> for NullStorage 114where 115 C: Component + Serialize, 116{ 117 fn to_rusqlite(_component: C) -> Result<rusqlite::types::Value, StorageError> { 118 Ok(rusqlite::types::Value::Null) 119 } 120} 121 122pub trait Bundle { 123 const COMPONENTS: &'static [&'static str]; 124 fn component_names() -> &'static [&'static str] { 125 Self::COMPONENTS 126 } 127 128 fn to_rusqlite(self) -> Result<Vec<(&'static str, rusqlite::types::Value)>, StorageError>; 129} 130 131impl<C: Component> Bundle for C { 132 const COMPONENTS: &'static [&'static str] = &[C::NAME]; 133 134 fn to_rusqlite(self) -> Result<Vec<(&'static str, rusqlite::types::Value)>, StorageError> { 135 Ok(vec![(C::NAME, C::to_rusqlite(self)?)]) 136 } 137} 138 139macro_rules! bundle_tuple_impls { 140 ($t:tt, $($ts:tt),+) => { 141 impl<$t, $($ts,)+> Bundle for ($t, $($ts,)+) 142 where 143 $t: Component, 144 $($ts: Component,)+ 145 { 146 const COMPONENTS: &'static [&'static str] = &[ 147 $t::NAME, 148 $($ts::NAME,)+ 149 ]; 150 151 fn to_rusqlite( 152 self 153 ) -> Result<Vec<(&'static str, rusqlite::types::Value)>, StorageError> { 154 #[allow(non_snake_case)] 155 let ($t, $($ts,)+) = self; 156 Ok( 157 vec![ 158 ($t::NAME, $t::to_rusqlite($t)?), 159 $(($ts::NAME, $ts::to_rusqlite($ts)?),)+ 160 ] 161 ) 162 } 163 } 164 165 166 bundle_tuple_impls!($($ts),+); 167 }; 168 ($t:tt) => { 169 impl<$t: Component> Bundle for ($t,) { 170 const COMPONENTS: &'static [&'static str] = &[ $t::NAME ]; 171 172 fn to_rusqlite( 173 self 174 ) -> Result<Vec<(&'static str, rusqlite::types::Value)>, StorageError> { 175 let (t,) = self; 176 Ok( 177 vec![($t::NAME, $t::to_rusqlite(t)?)] 178 ) 179 } 180 } 181 182 }; 183} 184 185bundle_tuple_impls!(A, B, C);