1use crate::{Prefab, PrefabError, PrefabValue};
4use intuicio_data::type_hash::TypeHash;
5use serde::{Deserialize, Serialize};
6use std::{
7 any::{Any, type_name},
8 collections::HashMap,
9};
10
11type PropsSerializeFactory =
12 Box<dyn Fn(&dyn PropsData) -> Result<PrefabValue, PrefabError> + Send + Sync>;
13type PropsDeserializeFactory =
14 Box<dyn Fn(PrefabValue, &mut Props) -> Result<(), PrefabError> + Send + Sync>;
15
16#[derive(Default)]
17pub struct PropsRegistry {
18 type_mapping: HashMap<TypeHash, String>,
19 factories: HashMap<String, (PropsSerializeFactory, PropsDeserializeFactory)>,
20}
21
22impl PropsRegistry {
23 pub fn register_factory<T>(&mut self, name: &str)
24 where
25 T: 'static + Prefab + PropsData,
26 {
27 let s: PropsSerializeFactory = Box::new(move |data| {
28 if let Some(data) = data.as_any().downcast_ref::<T>() {
29 data.to_prefab()
30 } else {
31 Err(PrefabError::CouldNotSerialize(
32 "Could not downcast to concrete type!".to_owned(),
33 ))
34 }
35 });
36 let d: PropsDeserializeFactory = Box::new(move |data, props| {
37 props.write(T::from_prefab(data)?);
38 Ok(())
39 });
40 self.factories.insert(name.to_owned(), (s, d));
41 self.type_mapping
42 .insert(TypeHash::of::<T>(), name.to_owned());
43 }
44
45 pub fn unregister_factory(&mut self, name: &str) {
46 self.factories.remove(name);
47 }
48
49 pub fn serialize(&self, props: &Props) -> Result<PrefabValue, PrefabError> {
50 let mut group = PropsGroupPrefab::default();
51 for (t, p) in &props.0 {
52 if let Some(name) = self.type_mapping.get(t) {
53 if let Some(factory) = self.factories.get(name) {
54 group.data.insert(name.to_owned(), (factory.0)(p.as_ref())?);
55 }
56 } else {
57 return Err(PrefabError::CouldNotSerialize(
58 "No type mapping found!".to_owned(),
59 ));
60 }
61 }
62 group.to_prefab()
63 }
64
65 pub fn deserialize(&self, data: PrefabValue) -> Result<Props, PrefabError> {
66 let data = if data.is_null() {
67 PropsGroupPrefab::default()
68 } else {
69 PropsGroupPrefab::from_prefab(data)?
70 };
71 let mut props = Props::default();
72 for (key, value) in data.data {
73 if let Some(factory) = self.factories.get(&key) {
74 (factory.1)(value, &mut props)?;
75 } else {
76 return Err(PrefabError::CouldNotDeserialize(format!(
77 "Could not find properties factory: {key:?}"
78 )));
79 }
80 }
81 Ok(props)
82 }
83}
84
85#[derive(Debug, Clone)]
86pub enum PropsError {
87 CouldNotReadData,
88 HasNoDataOfType(String),
89}
90
91impl Prefab for PrefabValue {}
92
93impl PropsData for PrefabValue
94where
95 Self: Clone,
96{
97 fn clone_props(&self) -> Box<dyn PropsData> {
98 Box::new(self.clone())
99 }
100
101 fn as_any(&self) -> &dyn Any {
102 self
103 }
104}
105
106#[derive(Debug, Default, Clone, Serialize, Deserialize)]
107pub struct PropsGroupPrefab {
108 #[serde(default)]
109 #[serde(skip_serializing_if = "HashMap::is_empty")]
110 pub data: HashMap<String, PrefabValue>,
111}
112
113impl Prefab for PropsGroupPrefab {}
114
115impl PropsData for PropsGroupPrefab
116where
117 Self: Clone,
118{
119 fn clone_props(&self) -> Box<dyn PropsData> {
120 Box::new(self.clone())
121 }
122
123 fn as_any(&self) -> &dyn Any {
124 self
125 }
126}
127
128pub trait PropsData: Any + std::fmt::Debug + Send + Sync {
129 fn clone_props(&self) -> Box<dyn PropsData>;
130 fn as_any(&self) -> &dyn Any;
131
132 fn type_hash(&self) -> TypeHash {
133 TypeHash::of::<Self>()
134 }
135}
136
137impl Clone for Box<dyn PropsData> {
138 fn clone(&self) -> Self {
139 self.clone_props()
140 }
141}
142
143#[derive(Default, Clone)]
144pub struct Props(HashMap<TypeHash, Box<dyn PropsData>>);
145
146impl Props {
147 pub fn new<T>(data: T) -> Self
148 where
149 T: 'static + PropsData,
150 {
151 let mut result = HashMap::with_capacity(1);
152 result.insert(TypeHash::of::<T>(), Box::new(data) as Box<dyn PropsData>);
153 Self(result)
154 }
155
156 pub fn is_empty(&self) -> bool {
157 self.0.is_empty()
158 }
159
160 pub fn has<T>(&self) -> bool
161 where
162 T: 'static + PropsData,
163 {
164 let e = TypeHash::of::<T>();
165 self.0.iter().any(|(t, _)| *t == e)
166 }
167
168 pub fn remove<T>(&mut self)
169 where
170 T: 'static + PropsData,
171 {
172 self.0.remove(&TypeHash::of::<T>());
173 }
174
175 pub(crate) unsafe fn remove_by_type(&mut self, id: TypeHash) {
176 self.0.remove(&id);
177 }
178
179 pub fn consume<T>(&mut self) -> Result<Box<dyn PropsData>, PropsError>
180 where
181 T: 'static + PropsData,
182 {
183 if let Some(v) = self.0.remove(&TypeHash::of::<T>()) {
184 Ok(v)
185 } else {
186 Err(PropsError::HasNoDataOfType(type_name::<T>().to_owned()))
187 }
188 }
189
190 pub fn consume_unwrap_cloned<T>(&mut self) -> Result<T, PropsError>
191 where
192 T: 'static + PropsData + Clone,
193 {
194 if let Some(data) = self.consume::<T>()?.as_any().downcast_ref::<T>() {
195 Ok(data.clone())
196 } else {
197 Err(PropsError::CouldNotReadData)
198 }
199 }
200
201 pub fn read<T>(&self) -> Result<&T, PropsError>
202 where
203 T: 'static + PropsData,
204 {
205 let e = TypeHash::of::<T>();
206 if let Some((_, v)) = self.0.iter().find(|(t, _)| **t == e) {
207 if let Some(data) = v.as_any().downcast_ref::<T>() {
208 Ok(data)
209 } else {
210 Err(PropsError::CouldNotReadData)
211 }
212 } else {
213 Err(PropsError::HasNoDataOfType(type_name::<T>().to_owned()))
214 }
215 }
216
217 pub fn map_or_default<T, R, F>(&self, mut f: F) -> R
218 where
219 T: 'static + PropsData,
220 R: Default,
221 F: FnMut(&T) -> R,
222 {
223 match self.read() {
224 Ok(data) => f(data),
225 Err(_) => R::default(),
226 }
227 }
228
229 pub fn map_or_else<T, R, F, E>(&self, mut f: F, mut e: E) -> R
230 where
231 T: 'static + PropsData,
232 F: FnMut(&T) -> R,
233 E: FnMut() -> R,
234 {
235 match self.read() {
236 Ok(data) => f(data),
237 Err(_) => e(),
238 }
239 }
240
241 pub fn read_cloned<T>(&self) -> Result<T, PropsError>
242 where
243 T: 'static + PropsData + Clone,
244 {
245 self.read::<T>().cloned()
246 }
247
248 pub fn read_cloned_or_default<T>(&self) -> T
249 where
250 T: 'static + PropsData + Clone + Default,
251 {
252 self.read_cloned().unwrap_or_default()
253 }
254
255 pub fn read_cloned_or_else<T, F>(&self, mut f: F) -> T
256 where
257 T: 'static + PropsData + Clone + Default,
258 F: FnMut() -> T,
259 {
260 self.read_cloned().unwrap_or_else(|_| f())
261 }
262
263 pub fn write<T>(&mut self, data: T)
264 where
265 T: 'static + PropsData,
266 {
267 self.0
268 .insert(TypeHash::of::<T>(), Box::new(data) as Box<dyn PropsData>);
269 }
270
271 pub fn mutate<T, F>(&mut self, mut f: F)
272 where
273 T: 'static + PropsData,
274 F: FnMut(&T) -> T,
275 {
276 if let Ok(data) = self.read() {
277 let data = f(data);
278 self.write(data);
279 }
280 }
281
282 pub fn mutate_cloned<T, F>(&mut self, mut f: F)
283 where
284 T: 'static + PropsData + Clone,
285 F: FnMut(&mut T),
286 {
287 if let Ok(data) = self.read::<T>() {
288 let mut data = data.clone();
289 f(&mut data);
290 self.write(data);
291 }
292 }
293
294 pub fn mutate_or_write<T, F, W>(&mut self, mut f: F, mut w: W)
295 where
296 T: 'static + PropsData,
297 F: FnMut(&T) -> T,
298 W: FnMut() -> T,
299 {
300 if let Ok(data) = self.read() {
301 let data = f(data);
302 self.write(data);
303 } else {
304 let data = w();
305 self.write(data);
306 }
307 }
308
309 pub fn with<T>(mut self, data: T) -> Self
310 where
311 T: 'static + PropsData,
312 {
313 self.write(data);
314 self
315 }
316
317 pub fn without<T>(mut self) -> Self
318 where
319 T: 'static + PropsData,
320 {
321 self.0.remove(&TypeHash::of::<T>());
322 self
323 }
324
325 pub fn merge(self, other: Self) -> Self {
326 let mut result = self.into_inner();
327 result.extend(other.into_inner());
328 Self(result)
329 }
330
331 pub fn merge_from(&mut self, other: Self) {
332 self.0.extend(other.into_inner());
333 }
334
335 pub(crate) fn into_inner(self) -> HashMap<TypeHash, Box<dyn PropsData>> {
336 self.0
337 }
338}
339
340impl std::fmt::Debug for Props {
341 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 f.write_str("Props ")?;
343 f.debug_set().entries(self.0.values()).finish()
344 }
345}
346
347impl<T> From<T> for Props
348where
349 T: 'static + PropsData,
350{
351 fn from(data: T) -> Self {
352 Self::new(data)
353 }
354}
355
356impl From<&Self> for Props {
357 fn from(data: &Self) -> Self {
358 data.clone()
359 }
360}
361
362#[macro_export]
368macro_rules! implement_props_data {
369 ($type_name:ty) => {
370 impl $crate::props::PropsData for $type_name
371 where
372 Self: Clone,
373 {
374 fn clone_props(&self) -> Box<dyn $crate::props::PropsData> {
375 Box::new(self.clone())
376 }
377
378 fn as_any(&self) -> &dyn std::any::Any {
379 self
380 }
381 }
382
383 impl $crate::Prefab for $type_name {}
384 };
385}
386
387implement_props_data!(());
388implement_props_data!(i8);
389implement_props_data!(i16);
390implement_props_data!(i32);
391implement_props_data!(i64);
392implement_props_data!(i128);
393implement_props_data!(u8);
394implement_props_data!(u16);
395implement_props_data!(u32);
396implement_props_data!(u64);
397implement_props_data!(u128);
398implement_props_data!(f32);
399implement_props_data!(f64);
400implement_props_data!(isize);
401implement_props_data!(usize);
402implement_props_data!(bool);
403implement_props_data!(String);
404
405macro_rules! impl_tuple_props_conversion {
406 ($($id:ident),+) => {
407 #[allow(non_snake_case)]
408 impl<$($id: $crate::props::PropsData),+> From<($($id,)+)> for $crate::props::Props {
409 fn from(($($id,)+): ($($id,)+)) -> $crate::props::Props {
410 let mut result = std::collections::HashMap::default();
411 $(
412 result.insert(
413 $crate::TypeHash::of::<$id>(),
414 Box::new($id) as Box<dyn $crate::props::PropsData>,
415 );
416 )+
417 Self(result)
418 }
419 }
420
421 #[allow(non_snake_case)]
422 impl<$($id: $crate::props::PropsData + Clone + Default),+> From<&$crate::props::Props> for ($($id,)+) {
423 fn from(props: &$crate::props::Props) -> ($($id,)+) {
424 ( $( props.read_cloned_or_default::<$id>(), )+ )
425 }
426 }
427 };
428}
429
430impl_tuple_props_conversion!(A);
431impl_tuple_props_conversion!(A, B);
432impl_tuple_props_conversion!(A, B, C);
433impl_tuple_props_conversion!(A, B, C, D);
434impl_tuple_props_conversion!(A, B, C, D, E);
435impl_tuple_props_conversion!(A, B, C, D, E, F);
436impl_tuple_props_conversion!(A, B, C, D, E, F, G);
437impl_tuple_props_conversion!(A, B, C, D, E, F, G, H);
438impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I);
439impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J);
440impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K);
441impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L);
442impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M);
443impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
444impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
445impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
446impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
447impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
448impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
449impl_tuple_props_conversion!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
450impl_tuple_props_conversion!(
451 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U
452);
453impl_tuple_props_conversion!(
454 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V
455);
456impl_tuple_props_conversion!(
457 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, X
458);
459impl_tuple_props_conversion!(
460 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, X, Y
461);
462impl_tuple_props_conversion!(
463 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, X, Y, Z
464);