zcomponents/
lib.rs

1//! # ZComponents - a stupid component storage
2//!
3//! I find "serious" ECS to be an overkill for turn-based game logic,
4//! so I've created this simple library that does only one thing:
5//! stores your components.
6//!
7//! ## Example:
8//!
9//! ```rust
10//! use zcomponents::zcomponents_storage;
11//!
12//! #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, Default)]
13//! pub struct Id(i32);
14//!
15//! #[derive(Clone, Debug)]
16//! pub struct A {
17//!     value: i32,
18//! }
19//!
20//! #[derive(Clone, Debug)]
21//! pub struct B {
22//!     value: i32,
23//! }
24//!
25//! #[derive(Clone, Debug)]
26//! pub struct C;
27//!
28//! zcomponents_storage!(Storage<Id>: {
29//!     a: A,
30//!     b: B,
31//!     c: C,
32//! });
33//!
34//! // Create a new storage instance.
35//! let mut storage = Storage::new();
36//!
37//! // It doesn't store anything yet.
38//! assert_eq!(storage.ids().count(), 0);
39//!
40//! // Allocate a new id.
41//! let id0 = storage.alloc_id();
42//! assert_eq!(storage.ids().count(), 1);
43//!
44//! // This Entity doesn't have any components assigned.
45//! assert!(!storage.is_exist(id0));
46//!
47//! storage.a.insert(id0, A { value: 0 });
48//!
49//! // Now it has a component.
50//! assert!(storage.is_exist(id0));
51//!
52//! // Allocate another id.
53//! let id1 = storage.alloc_id();
54//! assert_eq!(storage.ids().count(), 2);
55//!
56//! storage.a.insert(id1, A { value: 1 });
57//! storage.b.insert(id1, B { value: 1 });
58//!
59//! // Iterate over everything.
60//! for id in storage.ids_collected() {
61//!     // We are not sure that this entity has the component,
62//!     // so we must use `get_opt`/`get_opt_mut` methods.
63//!     if let Some(a) = storage.a.get_opt_mut(id) {
64//!         a.value += 1;
65//!     }
66//!     if let Some(b) = storage.b.get_opt_mut(id) {
67//!         b.value += 1;
68//!         storage.c.insert(id, C);
69//!     }
70//! }
71//!
72//! // Iterate over `a` components.
73//! for id in storage.a.ids_collected() {
74//!     // Since we are sure that component exists,
75//!     // we can just use `get`/`get_mut` version:
76//!     storage.a.get_mut(id).value += 1;
77//! }
78//!
79//! // Remove the component
80//! storage.a.remove(id0);
81//!
82//! // Remove the whole entity
83//! storage.remove(id0);
84//!
85//! assert!(!storage.is_exist(id0));
86//! ```
87
88use std::{
89    collections::{hash_map, HashMap},
90    default::Default,
91    fmt::Debug,
92    hash::Hash,
93};
94
95#[derive(Debug, Clone)]
96pub struct ComponentContainer<Id: Hash + Eq, V> {
97    data: HashMap<Id, V>,
98}
99
100impl<Id: Hash + Eq + Copy + Debug, V: Clone> Default for ComponentContainer<Id, V> {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106impl<Id: Hash + Eq + Copy + Debug, V: Clone> ComponentContainer<Id, V> {
107    pub fn new() -> Self {
108        let data = HashMap::new();
109        Self { data }
110    }
111
112    pub fn get_opt(&self, id: Id) -> Option<&V> {
113        self.data.get(&id)
114    }
115
116    /// Note: panics if there's no such entity.
117    pub fn get(&self, id: Id) -> &V {
118        self.get_opt(id)
119            .unwrap_or_else(|| panic!("Can't find {:?} id", id))
120    }
121
122    pub fn get_opt_mut(&mut self, id: Id) -> Option<&mut V> {
123        self.data.get_mut(&id)
124    }
125
126    /// Note: panics if there's no such entity.
127    pub fn get_mut(&mut self, id: Id) -> &mut V {
128        self.get_opt_mut(id)
129            .unwrap_or_else(|| panic!("Can't find {:?} id", id))
130    }
131
132    /// Store a given data value under a given entity id of a stupid component
133    /// if no value is already stored under that entity's id.
134    pub fn insert(&mut self, id: Id, data: V) {
135        assert!(self.get_opt(id).is_none());
136        self.data.insert(id, data);
137    }
138
139    /// Note: panics if there's no such entity.
140    pub fn remove(&mut self, id: Id) {
141        assert!(self.get_opt(id).is_some());
142        self.data.remove(&id);
143    }
144
145    pub fn ids(&self) -> IdIter<'_, Id, V> {
146        IdIter::new(&self.data)
147    }
148
149    /// Note: Allocates Vec in heap.
150    pub fn ids_collected(&self) -> Vec<Id> {
151        self.ids().collect()
152    }
153}
154
155#[derive(Clone, Debug)]
156pub struct IdIter<'a, Id, V> {
157    iter: hash_map::Iter<'a, Id, V>,
158}
159
160impl<'a, Id: Eq + Hash + Clone + 'a, V: 'a> IdIter<'a, Id, V> {
161    pub fn new(map: &'a HashMap<Id, V>) -> Self {
162        Self { iter: map.iter() }
163    }
164}
165
166impl<'a, Id: Copy + 'a, V> Iterator for IdIter<'a, Id, V> {
167    type Item = Id;
168
169    fn next(&mut self) -> Option<Self::Item> {
170        if let Some((&id, _)) = self.iter.next() {
171            Some(id)
172        } else {
173            None
174        }
175    }
176}
177
178#[macro_export]
179macro_rules! zcomponents_storage {
180    ($struct_name:ident<$id_type:ty>: { $($component:ident: $t:ty,)* } ) => {
181        use std::collections::HashMap;
182
183        #[derive(Clone, Debug)]
184        pub struct $struct_name {
185            $(
186                pub $component: $crate::ComponentContainer<$id_type, $t>,
187            )*
188            next_obj_id: $id_type,
189            ids: HashMap<$id_type, ()>,
190        }
191
192        #[allow(dead_code)]
193        impl $struct_name {
194            pub fn new() -> Self {
195                Self {
196                    $(
197                        $component: $crate::ComponentContainer::new(),
198                    )*
199                    next_obj_id: Default::default(),
200                    ids: HashMap::new(),
201                }
202            }
203
204            pub fn alloc_id(&mut self) -> $id_type {
205                let id = self.next_obj_id;
206                self.next_obj_id.0 += 1;
207                self.ids.insert(id, ());
208                id
209            }
210
211            pub fn ids(&self) -> $crate::IdIter<$id_type, ()> {
212                $crate::IdIter::new(&self.ids)
213            }
214
215            pub fn ids_collected(&self) -> Vec<$id_type> {
216                self.ids().collect()
217            }
218
219            pub fn is_exist(&self, id: $id_type) -> bool {
220                $(
221                    if self.$component.get_opt(id).is_some() {
222                        return true;
223                    }
224                )*
225                false
226            }
227
228            pub fn remove(&mut self, id: $id_type) {
229                $(
230                    if self.$component.get_opt(id).is_some() {
231                        self.$component.remove(id);
232                    }
233                )*
234            }
235
236            pub fn debug_string(&self, id: $id_type) -> String {
237                let mut s = String::new();
238                $(
239                    if let Some(component) = self.$component.get_opt(id) {
240                        s.push_str(&format!("{:?} ", component));
241                    }
242                )*
243                s
244            }
245        }
246    }
247}