entity_trait_system/
lib.rs

1// specialization feature is incomplete, but is being used here in a very
2// limited but required capacity. it is only used to check if a type implements
3// a trait - there is no overlapping impl weirdness that is relied on
4#![allow(incomplete_features)]
5#![feature(specialization)]
6#![forbid(unsafe_code)]
7
8// macro is expanded at call site - makes it available to the lib user
9#[cfg(feature = "serde")]
10#[doc(hidden)]
11pub use once_cell;
12#[doc(hidden)]
13pub use paste;
14#[cfg(feature = "rayon")]
15#[doc(hidden)]
16pub use rayon;
17#[cfg(feature = "serde")]
18#[doc(hidden)]
19pub use serde;
20#[doc(hidden)]
21pub use slotmap;
22
23// macro is expanded at call site - must be pub
24#[doc(hidden)]
25pub trait IsType<T> {
26    const VALUE: bool = false;
27}
28
29impl<A, B> IsType<B> for A {
30    default const VALUE: bool = false;
31}
32
33// specialize for equal types
34impl<T> IsType<T> for T {
35    const VALUE: bool = true;
36}
37
38#[doc(hidden)]
39pub trait ArenaCast<T> {
40    fn cast(&self) -> &slotmap::DenseSlotMap<slotmap::DefaultKey, T>;
41    fn cast_mut(&mut self) -> &mut slotmap::DenseSlotMap<slotmap::DefaultKey, T>;
42}
43
44// default: panic for mismatched types
45impl<A, B> ArenaCast<B> for slotmap::DenseSlotMap<slotmap::DefaultKey, A> {
46    default fn cast(&self) -> &slotmap::DenseSlotMap<slotmap::DefaultKey, B> {
47        panic!( // never, gated at compile time
48            "Arena type mismatch: {} cannot be cast to {}",
49            std::any::type_name::<A>(),
50            std::any::type_name::<B>()
51        );
52    }
53
54    default fn cast_mut(&mut self) -> &mut slotmap::DenseSlotMap<slotmap::DefaultKey, B> {
55        panic!( // never, gated at compile time
56            "Arena type mismatch: {} cannot be cast to {}",
57            std::any::type_name::<A>(),
58            std::any::type_name::<B>()
59        );
60    }
61}
62
63// specialization: types match
64impl<T> ArenaCast<T> for slotmap::DenseSlotMap<slotmap::DefaultKey, T> {
65    fn cast(&self) -> &slotmap::DenseSlotMap<slotmap::DefaultKey, T> {
66        self
67    }
68
69    fn cast_mut(&mut self) -> &mut slotmap::DenseSlotMap<slotmap::DefaultKey, T> {
70        self
71    }
72}
73
74// conditional serde if specific type is also serde
75#[cfg(feature = "serde")]
76#[doc(hidden)] // must be exposed to caller from macro expansion
77pub trait SerdeArena<'de> {
78    // member serialize into a SerializeStruct (called from world Serialize impl)
79    fn serialize_arena<S>(&self, field_name: &'static str, state: &mut S) -> Result<(), S::Error>
80    where
81        S: serde::ser::SerializeStruct;
82
83    // member deserialize from map access (JSON, etc.)
84    fn deserialize_arena<M>(map: &mut M) -> Result<Self, M::Error>
85    where
86        M: serde::de::MapAccess<'de>,
87        Self: Sized;
88
89    // sequence deserialize (e.g. bincode)
90    fn from_seq<V>(seq: &mut V, field_name: &str) -> Result<Self, V::Error>
91    where
92        V: serde::de::SeqAccess<'de>,
93        Self: Sized;
94
95    const ACTIVE: bool; // whether this arena participates in serde
96}
97
98// default: type does NOT implement serde => do nothing
99#[cfg(feature = "serde")]
100impl<'de, T> SerdeArena<'de> for slotmap::DenseSlotMap<slotmap::DefaultKey, T> {
101    default fn serialize_arena<S>(
102        &self,
103        _field_name: &'static str,
104        _state: &mut S,
105    ) -> Result<(), S::Error>
106    where
107        S: serde::ser::SerializeStruct,
108    {
109        Ok(())
110    }
111
112    default fn deserialize_arena<M>(_map: &mut M) -> Result<Self, M::Error>
113    where
114        M: serde::de::MapAccess<'de>,
115    {
116        Ok(slotmap::DenseSlotMap::new())
117    }
118
119    default fn from_seq<V>(_seq: &mut V, _field_name: &str) -> Result<Self, V::Error>
120    where
121        V: serde::de::SeqAccess<'de>,
122    {
123        Ok(slotmap::DenseSlotMap::new())
124    }
125
126    default const ACTIVE: bool = false;
127}
128
129// specialized: type implements serde Serialize + Deserialize
130#[cfg(feature = "serde")]
131impl<'de, T> SerdeArena<'de> for slotmap::DenseSlotMap<slotmap::DefaultKey, T>
132where
133    T: serde::Serialize + serde::Deserialize<'de>,
134{
135    fn serialize_arena<S>(&self, field_name: &'static str, state: &mut S) -> Result<(), S::Error>
136    where
137        S: serde::ser::SerializeStruct,
138    {
139        state.serialize_field(field_name, self)
140    }
141
142    fn deserialize_arena<M>(map: &mut M) -> Result<Self, M::Error>
143    where
144        M: serde::de::MapAccess<'de>,
145    {
146        map.next_value()
147    }
148
149    fn from_seq<V>(seq: &mut V, field_name: &str) -> Result<Self, V::Error>
150    where
151        V: serde::de::SeqAccess<'de>,
152    {
153        seq.next_element()?
154            .ok_or_else(|| serde::de::Error::custom(format!("Missing element for {}", field_name)))
155    }
156
157    const ACTIVE: bool = true;
158}
159
160// type erased view to arena of some type
161pub trait ErasedArena {
162    fn get_any(&self, key: slotmap::DefaultKey) -> Option<&dyn std::any::Any>;
163    fn get_any_mut(&mut self, key: slotmap::DefaultKey) -> Option<&mut dyn std::any::Any>;
164}
165
166impl<T: 'static> ErasedArena for slotmap::DenseSlotMap<slotmap::DefaultKey, T> {
167    fn get_any(&self, key: slotmap::DefaultKey) -> Option<&dyn std::any::Any> {
168        self.get(key).map(|e| e as &dyn std::any::Any)
169    }
170
171    fn get_any_mut(&mut self, key: slotmap::DefaultKey) -> Option<&mut dyn std::any::Any> {
172        self.get_mut(key).map(|e| e as &mut dyn std::any::Any)
173    }
174}
175
176#[macro_export]
177#[cfg(not(feature = "rayon"))]
178macro_rules! __world_define_rayon_trait_helpers {
179    ($( $trait_name:ident ),*) => {};
180}
181
182#[macro_export]
183#[cfg(feature = "rayon")]
184macro_rules! __world_define_rayon_trait_helpers {
185    ($( $trait_name:ident ),*) => {
186        $crate::paste::paste! {
187
188$(
189trait [<ParVisitIf $trait_name>]<T> {
190    fn par_visit_if_applicable<F>(arena: &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, handler: &F)
191    where
192        F: Fn(&dyn $trait_name) + Send + Sync;
193
194    fn par_visit_if_applicable_mut<F>(arena: &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, handler: &F)
195    where
196        F: Fn(&mut dyn $trait_name) + Send + Sync;
197}
198
199impl<T> [<ParVisitIf $trait_name>]<T> for () {
200    default fn par_visit_if_applicable<F>(_arena: &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, _handler: &F)
201    where F: Fn(&dyn $trait_name) + Send + Sync {}
202
203    default fn par_visit_if_applicable_mut<F>(_arena: &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, _handler: &F)
204    where F: Fn(&mut dyn $trait_name) + Send + Sync {}
205}
206
207impl<T> [<ParVisitIf $trait_name>]<T> for ()
208where
209    T: $trait_name + Send + Sync,
210{
211    fn par_visit_if_applicable<F>(arena: &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, handler: &F)
212    where F: Fn(&dyn $trait_name) + Send + Sync
213    {
214        use $crate::rayon::iter::IntoParallelRefIterator;
215        use $crate::rayon::iter::ParallelIterator;
216        arena
217            .values_as_slice()
218            .par_iter()
219            .for_each(|entity| handler(entity as &dyn $trait_name));
220    }
221
222    fn par_visit_if_applicable_mut<F>(arena: &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, handler: &F)
223    where F: Fn(&mut dyn $trait_name) + Send + Sync
224    {
225        use $crate::rayon::iter::IntoParallelRefMutIterator;
226        use $crate::rayon::iter::ParallelIterator;
227        arena
228            .values_as_mut_slice()
229            .par_iter_mut()
230            .for_each(|entity| handler(entity as &mut dyn $trait_name));
231    }
232}
233
234)*
235        }
236    };
237}
238
239#[macro_export]
240macro_rules! __world_define_visitors_common {
241    // https://stackoverflow.com/a/37754096/15534181
242    //
243    // generate visit_* functions per trait (non-parallel)
244    (@pass_entity_tuple $($trait_name:ident),* @ $entity_tuple:tt) => {
245        $crate::paste::paste! {
246            $(
247                #[allow(unused)]
248                pub fn [<visit_ $trait_name:snake>]<F>(&self, mut handler: F)
249                where
250                    F: FnMut(&dyn $trait_name)
251                {
252                    $crate::__world_define_visitors_common!(@use_entity_tuple $trait_name $entity_tuple self handler);
253                }
254
255                #[allow(unused)]
256                pub fn [<visit_mut_ $trait_name:snake>]<F>(&mut self, mut handler: F)
257                where
258                    F: FnMut(&mut dyn $trait_name)
259                {
260                    $crate::__world_define_visitors_common!(@use_entity_tuple_mut $trait_name $entity_tuple self handler);
261                }
262            )*
263        }
264    };
265
266    (@use_entity_tuple $trait_name:ident ($( $entity:ty ),*) $self_ident:ident $handler_ident:ident) => {
267        $crate::paste::paste! {
268            $(
269                <() as [<VisitIf $trait_name>]<$entity>>::visit_if_applicable(
270                    &$self_ident.[<$entity:snake>],
271                    &mut $handler_ident,
272                );
273            )*
274        }
275    };
276
277    (@use_entity_tuple_mut $trait_name:ident ($( $entity:ty ),*) $self_ident:ident $handler_ident:ident) => {
278        $crate::paste::paste! {
279            $(
280                <() as [<VisitIf $trait_name>]<$entity>>::visit_if_applicable_mut(
281                    &mut $self_ident.[<$entity:snake>],
282                    &mut $handler_ident,
283                );
284            )*
285        }
286    };
287}
288
289#[macro_export]
290#[cfg(not(feature = "rayon"))]
291macro_rules! __world_define_visitors {
292    // Non-rayon version simply forwards to the common macro
293    (@pass_entity_tuple $($trait_name:ident),* @ $entity_tuple:tt) => {
294        $crate::__world_define_visitors_common!(@pass_entity_tuple $($trait_name),* @ $entity_tuple);
295    };
296}
297
298#[macro_export]
299#[cfg(feature = "rayon")]
300macro_rules! __world_define_visitors {
301    // https://stackoverflow.com/a/37754096/15534181
302    //
303    // generate visit_* functions per trait
304    (@pass_entity_tuple $($trait_name:ident),* @ $entity_tuple:tt) => {
305        // non-parallel visit functions
306        $crate::__world_define_visitors_common!(@pass_entity_tuple $($trait_name),* @ $entity_tuple);
307
308        // parallel visit functions (added only when rayon feature enabled)
309        $crate::paste::paste! {
310            $(
311                #[allow(unused)]
312                pub fn [<par_visit_ $trait_name:snake>]<F>(&self, handler: F)
313                where
314                    F: Fn(&dyn $trait_name) + Send + Sync
315                {
316                    $crate::__world_define_visitors!(@use_entity_tuple_par $trait_name $entity_tuple self handler);
317                }
318
319                #[allow(unused)]
320                pub fn [<par_visit_mut_ $trait_name:snake>]<F>(&mut self, handler: F)
321                where
322                    F: Fn(&mut dyn $trait_name) + Send + Sync
323                {
324                    $crate::__world_define_visitors!(@use_entity_tuple_par_mut $trait_name $entity_tuple self handler);
325                }
326            )*
327        }
328    };
329
330    (@use_entity_tuple_par $trait_name:ident ($( $entity:ty ),*) $self_ident:ident $handler_ident:ident) => {
331        $crate::paste::paste! {
332            use $crate::rayon::scope;
333            scope(|s| {
334                $(
335                    let arena_ref = &$self_ident.[<$entity:snake>];
336                    s.spawn(|_| {
337                        <() as [<ParVisitIf $trait_name>]<$entity>>::par_visit_if_applicable(
338                            arena_ref,
339                            &$handler_ident,
340                        );
341                    });
342                )*
343
344            });
345        }
346    };
347
348    (@use_entity_tuple_par_mut $trait_name:ident ($( $entity:ty ),*) $self_ident:ident $handler_ident:ident) => {
349        $crate::paste::paste! {
350            use $crate::rayon::scope;
351            scope(|s| {
352                $(
353                    let arena_ref = &mut $self_ident.[<$entity:snake>];
354                    let handler_ref = &$handler_ident;
355                    s.spawn(|_| {
356                        <() as [<ParVisitIf $trait_name>]<$entity>>::par_visit_if_applicable_mut(
357                            arena_ref,
358                            handler_ref,
359                        );
360                    });
361                )*
362            });
363        }
364    };
365}
366
367#[macro_export]
368#[cfg(not(feature = "debug"))]
369macro_rules! __world_define_struct {
370    ($struct_name:ident, $( $entity:ty ),*) => {
371        $crate::paste::paste! {
372            #[derive(Default)]
373            #[allow(private_interfaces)] // member is pub even if underlying type isn't
374            pub struct $struct_name {
375                $(
376                    pub [<$entity:snake>]: $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity>,
377                )*
378            }
379        }
380    };
381}
382
383#[macro_export]
384#[cfg(feature = "debug")]
385macro_rules! __world_define_struct {
386    ($struct_name:ident, $( $entity:ty ),*) => {
387        $crate::paste::paste! {
388            #[derive(Default, Debug)]
389            #[allow(private_interfaces)] // member is pub even if underlying type isn't
390            pub struct $struct_name {
391                $(
392                    pub [<$entity:snake>]: $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity>,
393                )*
394            }
395        }
396    };
397}
398
399#[macro_export]
400#[cfg(not(feature = "serde"))]
401macro_rules! __world_serde_support {
402    ($struct_name:ident, $( $entity:ty ),*) => {};
403}
404
405#[macro_export]
406#[cfg(feature = "serde")]
407macro_rules! __world_serde_support {
408    ($struct_name:ident, $( $entity:ty ),*) => {
409        $crate::paste::paste! {
410
411impl $crate::serde::Serialize for [<$struct_name ArenaID>] {
412    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
413    where
414        S: $crate::serde::Serializer,
415    {
416        let s = match self.0 {
417            $(
418                i if i == $struct_name::arena_id::<$entity>().0 => stringify!($entity),
419            )*
420            // impossible! could be a panic here instead
421            _ => return Err($crate::serde::ser::Error::custom(format!("Unknown ArenaID {}", self.0))),
422        };
423        serializer.serialize_str(s)
424    }
425}
426
427impl<'de> $crate::serde::Deserialize<'de> for [<$struct_name ArenaID>] {
428    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
429    where
430        D: $crate::serde::Deserializer<'de>,
431    {
432        let s = String::deserialize(deserializer)?;
433        let id = match s.as_str() {
434            $(
435                stringify!($entity) => $struct_name::arena_id::<$entity>().0,
436            )*
437            // possible! suppose a different world loads it in, and there isn't that type.
438            _ => return Err($crate::serde::de::Error::custom(format!("Unknown ArenaID string {}", s))),
439        };
440        Ok([<$struct_name ArenaID>](id))
441    }
442}
443
444impl $crate::serde::ser::Serialize for $struct_name {
445    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
446    where
447        S: $crate::serde::ser::Serializer,
448    {
449        use $crate::SerdeArena;
450        use $crate::serde::ser::SerializeStruct;
451
452        let field_count = 0 $( + if <$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity> as $crate::SerdeArena<'static>>::ACTIVE { 1 } else { 0 } )*;
453        let mut state = serializer.serialize_struct(
454            stringify!($struct_name),
455            field_count
456        )?;
457        $(
458            self.[<$entity:snake>].serialize_arena(stringify!($entity), &mut state)?;
459        )*
460        state.end()
461    }
462}
463
464// mut be 'static, and can't do this at compile time :(
465static [<$struct_name:upper _DESERIALIZE_FIELDS>]: $crate::once_cell::sync::Lazy<Vec<&'static str>> =
466    $crate::once_cell::sync::Lazy::new(|| {
467        vec![
468            $(
469                if <$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity> as $crate::SerdeArena<'static>>::ACTIVE {
470                    Some(stringify!($entity))
471                } else {
472                    None
473                }
474            ),*
475        ]
476        .into_iter()
477        .flatten() // converts Option<&str> -> only keep Some
478        .collect()
479    });
480
481
482impl<'de> $crate::serde::Deserialize<'de> for $struct_name {
483    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
484    where
485        D: $crate::serde::Deserializer<'de>,
486    {
487        use $crate::serde::de::{MapAccess, SeqAccess, Visitor, Error};
488        use std::fmt;
489
490        struct WorldVisitor;
491
492        impl<'de> Visitor<'de> for WorldVisitor {
493            type Value = $struct_name;
494
495            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
496                write!(f, "struct {}", stringify!($struct_name))
497            }
498
499            // JSON-style: map fields
500            fn visit_map<V>(self, mut map: V) -> Result<$struct_name, V::Error>
501            where
502                V: MapAccess<'de>,
503            {
504                let mut world = $struct_name::default();
505
506                while let Some(key) = map.next_key::<String>()? {
507                    match key.as_str() {
508                        $(
509                            stringify!($entity) => {
510                                world.[<$entity:snake>] =
511                                    <$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity> as $crate::SerdeArena<'de>>::deserialize_arena(&mut map)?;
512                            }
513                        )*
514                        other => {
515                            return Err(V::Error::custom(format!(
516                                "Unknown field '{}' for {}",
517                                other,
518                                stringify!($struct_name)
519                            )));
520                        }
521                    }
522                }
523
524                Ok(world)
525            }
526
527            // Bincode-style: sequence fields.
528            // WARNING! Requires stated entities not changing order
529            fn visit_seq<V>(self, mut seq: V) -> Result<$struct_name, V::Error>
530            where
531                V: SeqAccess<'de>,
532            {
533               Ok($struct_name {
534                    $(
535                        [<$entity:snake>]: <$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity> as $crate::SerdeArena<'de>>::from_seq(&mut seq, stringify!($entity))?,
536                    )*
537                })
538            }
539        }
540
541        // Choose entry point depending on deserializer type
542        //
543        // JSON/CBOR: calls `deserialize_struct` -> `visit_map`
544        // Bincode: must call `deserialize_struct` directly (sequence)
545        deserializer.deserialize_struct(
546            stringify!($struct_name),
547            &[<$struct_name:upper _DESERIALIZE_FIELDS>],
548            WorldVisitor,
549        )
550    }
551}
552        }
553    };
554}
555
556#[macro_export]
557macro_rules! world {
558    // main macro form: define struct + traits + impl
559    (
560        // the name of the struct being defined
561        $struct_name:ident, // the types of entities which can exist in the world
562        $( $entity:ty ),* $(,)? // optional trailing comma
563        ;// semi colon separator between lists
564        // the traits which are query-able over all types in the world
565        $( $trait_name:ident ),* $(,)? // optional trailing comma
566    ) => {
567        $crate::paste::paste! {
568
569/// select type erased arena at runtime
570// instead of typeid, which is not stable between rust version. e.g.
571// serialization? this id is determined by the order stated by the user when
572// creating the world
573//
574// this is declared per world for safety - if serde is enabled and the entity
575// order changes, it still goes to the correct entity
576#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
577pub struct [<$struct_name ArenaID>](usize);
578
579$crate::__world_define_struct!($struct_name, $($entity),*);
580
581$(
582    trait [<VisitIf $trait_name>]<T> {
583        fn visit_if_applicable<F>(arena: &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, handler: F)
584        where
585            F: FnMut(&dyn $trait_name);
586
587        fn visit_if_applicable_mut<F>(arena: &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, handler: F)
588        where
589            F: FnMut(&mut dyn $trait_name);
590    }
591
592    // no-op for types not implementing the trait
593    impl<T> [<VisitIf $trait_name>]<T> for () {
594        default fn visit_if_applicable<F>(_arena: &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, _handler: F)
595        where F: FnMut(&dyn $trait_name) {}
596
597        default fn visit_if_applicable_mut<F>(_arena: &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, _handler: F)
598        where F: FnMut(&mut dyn $trait_name) {}
599    }
600
601    impl<T: $trait_name> [<VisitIf $trait_name>]<T> for () {
602        fn visit_if_applicable<F>(arena: &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, mut handler: F)
603        where F: FnMut(&dyn $trait_name)
604        {
605            arena
606                .values_as_slice()
607                .iter()
608                .for_each(|entity| handler(entity as &dyn $trait_name));
609        }
610
611        fn visit_if_applicable_mut<F>(arena: &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T>, mut handler: F)
612        where F: FnMut(&mut dyn $trait_name)
613        {
614            arena
615                .values_as_mut_slice()
616                .iter_mut()
617                .for_each(|entity| handler(entity as &mut dyn $trait_name));
618        }
619    }
620
621)*
622
623impl $struct_name {
624    $crate::__world_define_visitors!(@pass_entity_tuple $($trait_name),* @ ($($entity),*));
625
626    #[allow(unused)]
627    pub fn arena<T>(&self) -> &$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T> {
628        use $crate::ArenaCast;
629        $(
630            if <T as $crate::IsType<$entity>>::VALUE {
631                return <$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity> as ArenaCast<T>>::cast(&self.[<$entity:snake>]);
632            }
633        )*
634        panic!("In call to {}::arena::<{}>(), {} not registered", stringify!($struct_name), std::any::type_name::<T>(), std::any::type_name::<T>());
635    }
636
637    #[allow(unused)]
638    pub fn arena_mut<T>(&mut self) -> &mut $crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, T> {
639        use $crate::ArenaCast;
640        $(
641            if <T as $crate::IsType<$entity>>::VALUE {
642                return <$crate::slotmap::DenseSlotMap<$crate::slotmap::DefaultKey, $entity> as ArenaCast<T>>::cast_mut(&mut self.[<$entity:snake>]);
643            }
644        )*
645        panic!("In call to {}::arena_mut::<{}>(), {} not registered", stringify!($struct_name), std::any::type_name::<T>(), std::any::type_name::<T>());
646    }
647
648
649    pub fn arena_id<T>() -> [<$struct_name ArenaID>] {
650        let mut i = 0usize;
651        $(
652            if <T as $crate::IsType<$entity>>::VALUE {
653                return [<$struct_name ArenaID>](i);
654            }
655            i += 1;
656        )*
657        panic!("In call to {}::arena_id::<{}>(), {} not registered", stringify!($struct_name), std::any::type_name::<T>(), std::any::type_name::<T>());
658    }
659
660    #[allow(unused)]
661    pub fn arena_erased(&self, id: [<$struct_name ArenaID>]) -> &dyn $crate::ErasedArena {
662        match id.0 {
663            $(
664                i if i == Self::arena_id::<$entity>().0 => &self.[<$entity:snake>] as &dyn $crate::ErasedArena,
665            )*
666            _ => panic!("No arena for type id {}", id.0),
667        }
668    }
669
670    #[allow(unused)]
671    pub fn arena_erased_mut(&mut self, id: [<$struct_name ArenaID>]) -> &mut dyn $crate::ErasedArena {
672        match id.0 {
673            $(
674                i if i == Self::arena_id::<$entity>().0 => &mut self.[<$entity:snake>] as &mut dyn $crate::ErasedArena,
675            )*
676            _ => panic!("No arena for type id {}", id.0),
677        }
678    }
679}
680
681$crate::__world_serde_support!($struct_name, $($entity),*);
682
683$crate::__world_define_rayon_trait_helpers!($($trait_name),*);
684
685        }
686    };
687}
688
689// ---------------------------------------------------------------
690// Tests
691// ---------------------------------------------------------------
692#[cfg(test)]
693mod tests {
694    #[derive(Debug)]
695    #[cfg_attr(
696        feature = "serde",
697        derive(serde::Serialize, serde::Deserialize, bincode::Encode, bincode::Decode)
698    )]
699    struct Player {
700        id: u32,
701    }
702    #[derive(Debug)]
703    // each type individually can opt in to being serde
704    #[cfg_attr(
705        feature = "serde",
706        derive(serde::Serialize, serde::Deserialize, bincode::Encode, bincode::Decode)
707    )]
708    struct Enemy {
709        hp: u32,
710    }
711
712    pub trait TestTrait {
713        fn do_something(&self);
714    }
715    impl TestTrait for Player {
716        fn do_something(&self) {
717            println!("Player {}", self.id)
718        }
719    }
720    impl TestTrait for Enemy {
721        fn do_something(&self) {
722            println!("Enemy {}", self.hp)
723        }
724    }
725
726    pub trait SecondTestTrait {
727        fn do_something_else(&mut self);
728    }
729    impl SecondTestTrait for Player {
730        fn do_something_else(&mut self) {
731            println!("Player second trait")
732        }
733    }
734
735    world!(MyWorld, Enemy, Player; TestTrait, SecondTestTrait);
736
737    #[test]
738    fn do_tests() {
739        let mut world = MyWorld::default();
740        // directly access arena member
741        let player_id = world.player.insert(Player { id: 1 });
742        // compile time type accessor of arena member
743        world.arena_mut::<Enemy>().insert(Enemy { hp: 10 });
744
745        // world.arena_mut::<usize>();
746
747        // visit all arena with types that implement trait
748        #[cfg(feature = "rayon")]
749        world.par_visit_test_trait(|e| e.do_something());
750        #[cfg(not(feature = "rayon"))]
751        world.visit_test_trait(|e| e.do_something());
752
753        #[cfg(feature = "rayon")]
754        world.par_visit_mut_second_test_trait(|e| e.do_something_else());
755        #[cfg(not(feature = "rayon"))]
756        world.visit_mut_second_test_trait(|e| e.do_something_else());
757
758        // runtime type accessor. unique id is tuple (arena_id, arena_key).
759        // manage it however you decide!
760        let arena_id = MyWorld::arena_id::<Player>();
761        let arena = world.arena_erased(arena_id);
762        // unwrap: I know that this is a player and that the reference is valid
763        let player = arena
764            .get_any(player_id)
765            .unwrap()
766            .downcast_ref::<Player>()
767            .unwrap();
768        player.do_something();
769
770        #[cfg(all(feature = "serde", feature = "debug"))]
771        {
772            println!("testing serialization round trips");
773            // round trip different formats
774            let serialized = serde_json::to_string(&world).unwrap();
775            let deserialized_json: MyWorld = serde_json::from_str(&serialized).unwrap();
776
777            let serialized: Vec<u8> =
778                bincode::serde::encode_to_vec(&world, bincode::config::standard()).unwrap();
779            let (deserialized_bincode, _bytes_read): (MyWorld, usize) =
780                bincode::serde::decode_from_slice(&serialized, bincode::config::standard())
781                    .unwrap();
782
783            assert_eq!(format!("{:?}", world), format!("{:?}", deserialized_json));
784            assert_eq!(
785                format!("{:?}", world),
786                format!("{:?}", deserialized_bincode)
787            );
788        }
789    }
790}