naia_shared/world/resource/
resource_registry.rs1use std::{any::TypeId, collections::HashMap};
2
3use crate::GlobalEntity;
4
5#[derive(Clone, Debug, Default)]
22pub struct ResourceRegistry {
23 by_type: HashMap<TypeId, GlobalEntity>,
24 by_entity: HashMap<GlobalEntity, TypeId>,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct ResourceAlreadyExists;
31
32impl std::fmt::Display for ResourceAlreadyExists {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 write!(f, "resource of this type is already registered")
35 }
36}
37
38impl std::error::Error for ResourceAlreadyExists {}
39
40impl ResourceRegistry {
41 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn insert<R: 'static>(
51 &mut self,
52 entity: GlobalEntity,
53 ) -> Result<(), ResourceAlreadyExists> {
54 let type_id = TypeId::of::<R>();
55 if self.by_type.contains_key(&type_id) {
56 return Err(ResourceAlreadyExists);
57 }
58 self.by_type.insert(type_id, entity);
59 self.by_entity.insert(entity, type_id);
60 Ok(())
61 }
62
63 pub fn insert_raw(
69 &mut self,
70 type_id: TypeId,
71 entity: GlobalEntity,
72 ) -> Result<(), ResourceAlreadyExists> {
73 if let Some(existing) = self.by_type.get(&type_id) {
74 if *existing == entity {
75 return Ok(());
76 }
77 return Err(ResourceAlreadyExists);
78 }
79 self.by_type.insert(type_id, entity);
80 self.by_entity.insert(entity, type_id);
81 Ok(())
82 }
83
84 pub fn remove<R: 'static>(&mut self) -> Option<GlobalEntity> {
86 let type_id = TypeId::of::<R>();
87 let entity = self.by_type.remove(&type_id)?;
88 self.by_entity.remove(&entity);
89 Some(entity)
90 }
91
92 pub fn remove_by_entity(&mut self, entity: &GlobalEntity) -> Option<TypeId> {
95 let type_id = self.by_entity.remove(entity)?;
96 self.by_type.remove(&type_id);
97 Some(type_id)
98 }
99
100 pub fn entity_for<R: 'static>(&self) -> Option<GlobalEntity> {
102 self.by_type.get(&TypeId::of::<R>()).copied()
103 }
104
105 pub fn entity_for_raw(&self, type_id: &TypeId) -> Option<GlobalEntity> {
107 self.by_type.get(type_id).copied()
108 }
109
110 pub fn type_for(&self, entity: &GlobalEntity) -> Option<TypeId> {
112 self.by_entity.get(entity).copied()
113 }
114
115 pub fn is_resource_entity(&self, entity: &GlobalEntity) -> bool {
117 self.by_entity.contains_key(entity)
118 }
119
120 pub fn len(&self) -> usize {
122 self.by_type.len()
123 }
124
125 pub fn is_empty(&self) -> bool {
127 self.by_type.is_empty()
128 }
129
130 pub fn iter(&self) -> impl Iterator<Item = (&TypeId, &GlobalEntity)> {
134 self.by_type.iter()
135 }
136
137 pub fn entities(&self) -> impl Iterator<Item = &GlobalEntity> {
139 self.by_type.values()
140 }
141}
142
143#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::BigMapKey;
150
151 fn ge(n: u64) -> GlobalEntity {
152 GlobalEntity::from_u64(n)
153 }
154
155 fn tya() -> TypeId {
156 struct A;
157 TypeId::of::<A>()
158 }
159 fn tyb() -> TypeId {
160 struct B;
161 TypeId::of::<B>()
162 }
163
164 #[test]
165 fn insert_raw_and_lookup_both_directions() {
166 let mut r = ResourceRegistry::new();
167 let e = ge(1);
168 r.insert_raw(tya(), e).unwrap();
169
170 assert_eq!(r.entity_for_raw(&tya()), Some(e));
171 assert_eq!(r.type_for(&e), Some(tya()));
172 assert!(r.is_resource_entity(&e));
173 assert_eq!(r.len(), 1);
174 }
175
176 #[test]
177 fn double_insert_same_type_distinct_entity_errors() {
178 let mut r = ResourceRegistry::new();
179 r.insert_raw(tya(), ge(1)).unwrap();
180 assert_eq!(r.insert_raw(tya(), ge(2)), Err(ResourceAlreadyExists));
181 assert_eq!(r.entity_for_raw(&tya()), Some(ge(1)));
182 }
183
184 #[test]
185 fn insert_raw_idempotent_for_identical_pair() {
186 let mut r = ResourceRegistry::new();
187 let e = ge(7);
188 r.insert_raw(tya(), e).unwrap();
189 r.insert_raw(tya(), e).unwrap(); assert_eq!(r.insert_raw(tya(), ge(8)), Err(ResourceAlreadyExists));
191 }
192
193 #[test]
194 fn remove_by_entity_clears_both_indices() {
195 let mut r = ResourceRegistry::new();
196 let e = ge(4);
197 r.insert_raw(tya(), e).unwrap();
198 let ty = r.remove_by_entity(&e);
199 assert_eq!(ty, Some(tya()));
200 assert!(r.is_empty());
201 assert!(!r.is_resource_entity(&e));
202 }
203
204 #[test]
205 fn multi_type_isolation() {
206 let mut r = ResourceRegistry::new();
207 r.insert_raw(tya(), ge(1)).unwrap();
208 r.insert_raw(tyb(), ge(2)).unwrap();
209
210 assert_eq!(r.entity_for_raw(&tya()), Some(ge(1)));
211 assert_eq!(r.entity_for_raw(&tyb()), Some(ge(2)));
212 r.remove_by_entity(&ge(1));
213 assert_eq!(r.entity_for_raw(&tya()), None);
214 assert_eq!(r.entity_for_raw(&tyb()), Some(ge(2)));
215 }
216}