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);