my_ecs/ecs/ent/
component.rs

1use crate::ds::prelude::*;
2use my_ecs_macros::repeat_macro;
3
4/// Ordinary rust types.
5pub trait Component: Send + Sync + Sized + 'static {
6    const IS_SEND: bool = true; // by bound
7    const IS_SYNC: bool = true; // by bound
8
9    // === Must be overwritten by something like [`my_ecs_macros::Component`] ===
10    const IS_DEFAULT: bool = false; // depends on impl
11    const FN_DEFAULT: FnDefaultRaw = unimpl_default; // depends on impl
12    const IS_CLONE: bool = false; // depends on impl
13    const FN_CLONE: FnCloneRaw = unimpl_clone; // depends on impl
14
15    fn key() -> ComponentKey {
16        ComponentKey::of::<Self>()
17    }
18
19    fn type_info() -> TypeInfo {
20        TypeInfo::new::<Self>(
21            Self::IS_SEND,
22            Self::IS_SYNC,
23            Self::IS_DEFAULT.then_some(Self::FN_DEFAULT),
24            Self::IS_CLONE.then_some(Self::FN_CLONE),
25        )
26    }
27}
28
29/// A set of [`Component`]s.
30pub trait Components: 'static {
31    type Keys: AsRef<[ComponentKey]>;
32    type Infos: AsRef<[TypeInfo]>;
33
34    const LEN: usize;
35
36    /// Returns [`ComponentKey`]s in declared field order.
37    fn keys() -> Self::Keys;
38
39    /// Returns [`TypeInfo`]s in declared field order.
40    fn infos() -> Self::Infos;
41
42    /// Returns sorted [`ComponentKey`]s.
43    fn sorted_keys() -> Self::Keys;
44}
45
46macro_rules! impl_components {
47    ($n:expr, $($i:expr),*) => {const _: () = {
48        #[allow(unused_imports)]
49        use $crate::{
50            ds::TypeInfo,
51            ecs::ent::component::{Component, Components, ComponentKey},
52        };
53        use paste::paste;
54
55        paste! {
56            #[allow(unused_parens)]
57            impl<$([<A $i>]: Component),*> Components for ( $([<A $i>]),* ) {
58                type Keys = [ComponentKey; $n];
59                type Infos = [TypeInfo; $n];
60
61                const LEN: usize = $n;
62
63                fn keys() -> Self::Keys {
64                    [ $( [<A $i>]::key() ),* ]
65                }
66
67                fn infos() -> Self::Infos {
68                    [ $( [<A $i>]::type_info() ),* ]
69                }
70
71                fn sorted_keys() -> Self::Keys {
72                    let mut keys = [ $( [<A $i>]::key() ),* ];
73                    keys.sort_unstable();
74                    keys
75                }
76            }
77        }
78    };};
79}
80repeat_macro!(impl_components, ..=32);
81
82/// Unique identifier for a type implementing [`Component`].
83pub type ComponentKey = ATypeId<ComponentKey_>;
84pub struct ComponentKey_;
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate as my_ecs;
90    use my_ecs_macros::Component;
91
92    #[test]
93    fn test_component_detect_impls() {
94        #![allow(dead_code)]
95
96        #[derive(Component, Clone)]
97        struct CloneA([u8; 1]);
98        #[derive(Component, Clone)]
99        struct CloneB([u8; 2]);
100
101        #[derive(Component)]
102        struct NonCloneA([u8; 1]);
103        #[derive(Component)]
104        struct NonCloneB([u8; 2]);
105
106        // Non-cloneable components have the same clone function which causes
107        // panic.
108        assert!(is_clone_fn_eq::<NonCloneA, NonCloneB>());
109
110        // But cloneable components have different clone functions to each
111        // other.
112        assert!(is_clone_fn_ne::<CloneA, CloneB>());
113        assert!(is_clone_fn_ne::<CloneA, NonCloneA>());
114        assert!(is_clone_fn_ne::<CloneB, NonCloneA>());
115
116        // === Internal helper functions ===
117
118        fn is_clone_fn_eq<Ca: Component, Cb: Component>() -> bool {
119            let clone_a = Ca::FN_CLONE as usize;
120            let clone_b = Cb::FN_CLONE as usize;
121            clone_a == clone_b
122        }
123
124        fn is_clone_fn_ne<Ca: Component, Cb: Component>() -> bool {
125            let clone_a = Ca::FN_CLONE as usize;
126            let clone_b = Cb::FN_CLONE as usize;
127            clone_a != clone_b
128        }
129    }
130}