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}