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