1use alloc::vec::Vec;
2use soroban_sdk::{contracttype, Bytes, BytesN, Env, IntoVal, Symbol, TryFromVal, Val};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct ComponentId {
7 id: u32,
8}
9
10impl ComponentId {
11 pub fn new(id: u32) -> Self {
12 Self { id }
13 }
14 pub fn id(&self) -> u32 {
15 self.id
16 }
17}
18impl IntoVal<Env, Val> for ComponentId {
20 fn into_val(&self, env: &Env) -> Val {
21 self.id.into_val(env)
22 }
23}
24
25impl TryFromVal<Env, Val> for ComponentId {
26 type Error = soroban_sdk::ConversionError;
27
28 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
29 let id: u32 = TryFromVal::try_from_val(env, val)?;
30 Ok(ComponentId::new(id))
31 }
32}
33
34#[contracttype]
35#[repr(u32)]
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
37pub enum ComponentStorage {
38 #[default]
39 Table = 0,
40 Sparse = 1,
41}
42
43#[contracttype]
44#[derive(Debug, Clone)]
45pub struct Component {
46 pub component_type: Symbol,
47 pub data: Bytes,
48 pub storage: ComponentStorage,
49}
50
51impl Component {
52 pub fn new(component_type: Symbol, data: Bytes) -> Self {
53 Self {
54 component_type,
55 data,
56 storage: ComponentStorage::default(),
57 }
58 }
59 pub fn with_storage(component_type: Symbol, data: Bytes, storage: ComponentStorage) -> Self {
60 Self {
61 component_type,
62 data,
63 storage,
64 }
65 }
66 pub fn component_type(&self) -> &Symbol {
67 &self.component_type
68 }
69 pub fn data(&self) -> &Bytes {
70 &self.data
71 }
72 pub fn data_mut(&mut self) -> &mut Bytes {
73 &mut self.data
74 }
75 pub fn storage(&self) -> ComponentStorage {
76 self.storage
77 }
78 pub fn set_storage(&mut self, storage: ComponentStorage) {
79 self.storage = storage;
80 }
81}
82
83#[derive(Debug, Clone)]
85pub struct ComponentRegistry {
86 next_id: u32,
87 components: Vec<(Symbol, ComponentId)>,
88}
89
90impl ComponentRegistry {
91 pub fn new() -> Self {
93 Self {
94 next_id: 1,
95 components: Vec::new(),
96 }
97 }
98
99 pub fn register_component(&mut self, component_type: Symbol) -> ComponentId {
101 for (ctype, id) in &self.components {
103 if ctype == &component_type {
104 return *id;
105 }
106 }
107
108 let id = ComponentId::new(self.next_id);
109 self.next_id += 1;
110 self.components.push((component_type, id));
111 id
112 }
113
114 pub fn get_component_id(&self, component_type: &Symbol) -> Option<ComponentId> {
116 for (ctype, id) in &self.components {
117 if ctype == component_type {
118 return Some(*id);
119 }
120 }
121 None
122 }
123
124 pub fn get_component_type(&self, component_id: ComponentId) -> Option<Symbol> {
126 for (ctype, id) in &self.components {
127 if id == &component_id {
128 return Some(ctype.clone());
129 }
130 }
131 None
132 }
133
134 pub fn component_count(&self) -> usize {
136 self.components.len()
137 }
138
139 pub fn is_registered(&self, component_type: &Symbol) -> bool {
141 for (ctype, _) in &self.components {
142 if ctype == component_type {
143 return true;
144 }
145 }
146 false
147 }
148}
149
150impl Default for ComponentRegistry {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156pub trait ComponentTrait {
157 fn component_type() -> Symbol;
158 fn serialize(&self, env: &Env) -> Bytes;
159 fn deserialize(env: &Env, data: &Bytes) -> Option<Self>
160 where
161 Self: Sized;
162 fn default_storage() -> ComponentStorage {
163 ComponentStorage::Table
164 }
165}
166
167#[contracttype]
168#[derive(Clone)]
169pub struct Position {
170 pub x: i32,
171 pub y: i32,
172}
173impl Position {
174 pub fn new(x: i32, y: i32) -> Self {
175 Self { x, y }
176 }
177}
178impl_component!(Position, "position", Table, { x: i32, y: i32 });
179
180#[contracttype]
181#[derive(Clone)]
182pub struct Velocity {
183 pub x: i32,
184 pub y: i32,
185}
186impl Velocity {
187 pub fn new(x: i32, y: i32) -> Self {
188 Self { x, y }
189 }
190}
191impl_component!(Velocity, "velocity", Table, { x: i32, y: i32 });
192
193#[contracttype]
196#[derive(Clone, Debug)]
197pub struct Health {
198 pub current: u128,
199 pub max: u128,
200}
201impl_component!(Health, "health", Table, { current: u128, max: u128 });
202
203#[contracttype]
204#[derive(Clone, Debug)]
205pub struct Token {
206 pub amount: u32,
207 pub hash: BytesN<32>,
208}
209impl_component!(Token, "token", Table, { amount: u32, hash: bytes32 });
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use soroban_sdk::{symbol_short, BytesN, Env};
215
216 #[test]
217 fn test_component_id_creation() {
218 let id = ComponentId::new(1);
219 assert_eq!(id.id(), 1);
220 }
221
222 #[test]
223 fn test_component_creation() {
224 let env = Env::default();
225 let component_type = symbol_short!("test");
226 let mut data = Bytes::new(&env);
227 data.append(&Bytes::from_array(&env, &[1, 2, 3, 4]));
228 let component = Component::new(component_type, data.clone());
229
230 assert_eq!(component.component_type(), &symbol_short!("test"));
231 assert_eq!(component.data(), &data);
232 assert_eq!(component.storage(), ComponentStorage::Table);
233 }
234
235 #[test]
236 fn test_component_registry() {
237 let mut registry = ComponentRegistry::new();
238 assert_eq!(registry.component_count(), 0);
239
240 let component_type = symbol_short!("test");
241 let id = registry.register_component(component_type.clone());
242 assert_eq!(registry.component_count(), 1);
243 assert!(registry.is_registered(&component_type));
244
245 let retrieved_id = registry.get_component_id(&component_type);
246 assert_eq!(retrieved_id, Some(id));
247 }
248
249 #[test]
250 fn test_position_component() {
251 let env = Env::default();
252 let position = Position::new(100, 200);
253 let data = position.serialize(&env);
254 let deserialized = Position::deserialize(&env, &data).unwrap();
255
256 assert_eq!(position.x, deserialized.x);
257 assert_eq!(position.y, deserialized.y);
258 }
259
260 #[test]
263 fn test_u128_component() {
264 let env = Env::default();
265 let health = Health {
266 current: 999_999_999_999,
267 max: 1_000_000_000_000,
268 };
269 let data = health.serialize(&env);
270 assert_eq!(data.len(), 32); let deserialized = Health::deserialize(&env, &data).unwrap();
272 assert_eq!(deserialized.current, 999_999_999_999);
273 assert_eq!(deserialized.max, 1_000_000_000_000);
274 }
275
276 #[test]
277 fn test_bytes32_component() {
278 let env = Env::default();
279 let hash = BytesN::from_array(&env, &[0xABu8; 32]);
280 let token = Token {
281 amount: 42,
282 hash: hash.clone(),
283 };
284 let data = token.serialize(&env);
285 assert_eq!(data.len(), 36); let deserialized = Token::deserialize(&env, &data).unwrap();
287 assert_eq!(deserialized.amount, 42);
288 assert_eq!(deserialized.hash, hash);
289 }
290
291 #[test]
292 fn test_u128_zero() {
293 let env = Env::default();
294 let health = Health { current: 0, max: 0 };
295 let data = health.serialize(&env);
296 let deserialized = Health::deserialize(&env, &data).unwrap();
297 assert_eq!(deserialized.current, 0);
298 assert_eq!(deserialized.max, 0);
299 }
300
301 #[test]
302 fn test_u128_max() {
303 let env = Env::default();
304 let health = Health {
305 current: u128::MAX,
306 max: u128::MAX,
307 };
308 let data = health.serialize(&env);
309 let deserialized = Health::deserialize(&env, &data).unwrap();
310 assert_eq!(deserialized.current, u128::MAX);
311 assert_eq!(deserialized.max, u128::MAX);
312 }
313}