1use std::{fmt::Debug, marker::PhantomData, sync::Arc};
4
5use once_map::OnceMap;
6
7use crate::prelude::*;
8
9pub type AtomicUntypedResource = Arc<UntypedResource>;
11
12pub struct UntypedResource {
17 cell: AtomicCell<Option<SchemaBox>>,
18 schema: &'static Schema,
19}
20
21impl std::fmt::Debug for UntypedResource {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_struct("UntypedResource").finish_non_exhaustive()
24 }
25}
26
27impl UntypedResource {
28 pub fn empty(schema: &'static Schema) -> Self {
30 Self {
31 cell: AtomicCell::new(None),
32 schema,
33 }
34 }
35
36 pub fn new(resource: SchemaBox) -> Self {
38 Self {
39 schema: resource.schema(),
40 cell: AtomicCell::new(Some(resource)),
41 }
42 }
43
44 pub fn from_default(schema: &'static Schema) -> Self {
47 Self {
48 cell: AtomicCell::new(Some(SchemaBox::default(schema))),
49 schema,
50 }
51 }
52
53 pub fn clone_data(&self) -> Option<SchemaBox> {
56 (*self.cell.borrow()).clone()
57 }
58
59 pub fn insert(&self, data: SchemaBox) -> Result<Option<SchemaBox>, SchemaMismatchError> {
63 self.schema.ensure_match(data.schema())?;
64 let mut data = Some(data);
65 std::mem::swap(&mut data, &mut *self.cell.borrow_mut());
66 Ok(data)
67 }
68
69 pub fn remove(&self) -> Option<SchemaBox> {
71 let mut data = None;
72 std::mem::swap(&mut data, &mut *self.cell.borrow_mut());
73 data
74 }
75
76 #[track_caller]
78 pub fn borrow(&self) -> Ref<'_, Option<SchemaBox>> {
79 self.cell.borrow()
80 }
81
82 #[track_caller]
84 pub fn borrow_mut(&self) -> RefMut<'_, Option<SchemaBox>> {
85 self.cell.borrow_mut()
86 }
87
88 pub fn schema(&self) -> &'static Schema {
90 self.schema
91 }
92}
93
94#[derive(Default)]
101pub struct UntypedResources {
102 resources: OnceMap<SchemaId, AtomicUntypedResource>,
103 shared_resources: OnceMap<SchemaId, Box<()>>,
104}
105
106impl Clone for UntypedResources {
107 fn clone(&self) -> Self {
108 let binding = self.resources.read_only_view();
109 let resources = binding.iter().map(|(_, v)| (v.schema, v));
110
111 let new_resources = OnceMap::default();
112 let new_shared_resources = OnceMap::default();
113 for (schema, resource_cell) in resources {
114 let is_shared = self.shared_resources.contains_key(&schema.id());
115
116 if !is_shared {
117 let resource = resource_cell.clone_data();
118 new_resources.map_insert(
119 schema.id(),
120 |_| Arc::new(UntypedResource::empty(schema)),
121 |_, cell| {
122 if let Some(resource) = resource {
123 cell.insert(resource).unwrap();
124 }
125 },
126 );
127 } else {
128 new_shared_resources.insert(schema.id(), |_| Box::new(()));
129 new_resources.insert(schema.id(), |_| resource_cell.clone());
130 }
131 }
132 Self {
133 resources: new_resources,
134 shared_resources: new_shared_resources,
135 }
136 }
137}
138
139#[derive(Debug, Clone, Copy)]
141pub struct CellAlreadyPresentError;
142impl std::fmt::Display for CellAlreadyPresentError {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 f.write_str("Resource cell already present")
145 }
146}
147impl std::error::Error for CellAlreadyPresentError {}
148
149impl UntypedResources {
150 pub fn new() -> Self {
152 Self::default()
153 }
154
155 pub fn contains_cell(&self, id: SchemaId) -> bool {
157 self.resources.contains_key(&id)
158 }
159
160 pub fn contains(&self, id: SchemaId) -> bool {
162 self.resources
163 .map_get(&id, |_, cell| cell.borrow().is_some())
164 .unwrap_or_default()
165 }
166
167 pub fn insert_cell(&self, cell: AtomicUntypedResource) -> Result<(), CellAlreadyPresentError> {
179 let schema = cell.schema;
180 if self.resources.contains_key(&schema.id()) {
181 Err(CellAlreadyPresentError)
182 } else {
183 self.resources.insert(schema.id(), |_| cell);
184 self.shared_resources.insert(schema.id(), |_| Box::new(()));
185 Ok(())
186 }
187 }
188
189 pub fn get(&self, schema: &'static Schema) -> &UntypedResource {
191 self.resources
192 .insert(schema.id(), |_| Arc::new(UntypedResource::empty(schema)))
193 }
194
195 pub fn get_cell(&self, schema: &'static Schema) -> AtomicUntypedResource {
197 self.resources.map_insert(
198 schema.id(),
199 |_| Arc::new(UntypedResource::empty(schema)),
200 |_, cell| cell.clone(),
201 )
202 }
203
204 pub fn clear_owned_resources(&mut self) {
206 for (schema_id, resource_cell) in self.resources.iter_mut() {
207 let is_shared = self.shared_resources.contains_key(schema_id);
208 if !is_shared {
209 resource_cell.remove();
210 }
211 }
212 }
213}
214
215#[derive(Clone, Default)]
219pub struct Resources {
220 untyped: UntypedResources,
221}
222
223impl Resources {
224 pub fn new() -> Self {
226 Self::default()
227 }
228
229 pub fn insert<T: HasSchema>(&self, resource: T) -> Option<T> {
231 self.untyped
232 .get(T::schema())
233 .insert(SchemaBox::new(resource))
234 .unwrap()
235 .map(|x| x.cast_into())
236 }
237
238 pub fn contains<T: HasSchema>(&self) -> bool {
242 self.untyped.contains(T::schema().id())
243 }
244
245 pub fn remove<T: HasSchema>(&self) -> Option<T> {
247 self.untyped
248 .get(T::schema())
249 .remove()
250 .map(|x| unsafe { x.cast_into_unchecked() })
252 }
253
254 #[track_caller]
256 pub fn get<T: HasSchema>(&self) -> Option<Ref<'_, T>> {
257 let b = self.untyped.get(T::schema()).borrow();
258 if b.is_some() {
259 Some(Ref::map(b, |b| unsafe {
260 b.as_ref().unwrap().as_ref().cast_into_unchecked()
261 }))
262 } else {
263 None
264 }
265 }
266
267 #[track_caller]
269 pub fn get_mut<T: HasSchema>(&self) -> Option<RefMut<'_, T>> {
270 let b = self.untyped.get(T::schema()).borrow_mut();
271 if b.is_some() {
272 Some(RefMut::map(b, |b| unsafe {
273 b.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
274 }))
275 } else {
276 None
277 }
278 }
279
280 pub fn get_cell<T: HasSchema>(&self) -> AtomicResource<T> {
282 let untyped = self.untyped.get_cell(T::schema()).clone();
283 AtomicResource {
284 untyped,
285 _phantom: PhantomData,
286 }
287 }
288
289 pub fn untyped(&self) -> &UntypedResources {
291 &self.untyped
292 }
293
294 pub fn into_untyped(self) -> UntypedResources {
296 self.untyped
297 }
298
299 pub fn clear_owned_resources(&mut self) {
301 self.untyped.clear_owned_resources();
302 }
303}
304
305#[derive(Clone)]
312pub struct AtomicResource<T: HasSchema> {
313 untyped: AtomicUntypedResource,
314 _phantom: PhantomData<T>,
315}
316impl<T: HasSchema + Debug> Debug for AtomicResource<T> {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 f.write_str("AtomicResource(")?;
319 self.untyped
320 .cell
321 .borrow()
322 .as_ref()
323 .map(|x| x.cast_ref::<T>())
324 .fmt(f)?;
325 f.write_str(")")?;
326 Ok(())
327 }
328}
329
330impl<T: HasSchema + Default> Default for AtomicResource<T> {
331 fn default() -> Self {
332 Self {
333 untyped: Arc::new(UntypedResource::new(SchemaBox::new(T::default()))),
334 _phantom: Default::default(),
335 }
336 }
337}
338
339impl<T: HasSchema> AtomicResource<T> {
340 pub fn empty() -> Self {
342 Self {
343 untyped: Arc::new(UntypedResource::empty(T::schema())),
344 _phantom: PhantomData,
345 }
346 }
347
348 pub fn new(data: T) -> Self {
350 AtomicResource {
351 untyped: Arc::new(UntypedResource::new(SchemaBox::new(data))),
352 _phantom: PhantomData,
353 }
354 }
355
356 pub fn from_untyped(untyped: AtomicUntypedResource) -> Result<Self, SchemaMismatchError> {
358 T::schema().ensure_match(untyped.schema)?;
359 Ok(AtomicResource {
360 untyped,
361 _phantom: PhantomData,
362 })
363 }
364
365 pub fn remove(&self) -> Option<T> {
367 self.untyped
368 .remove()
369 .map(|x| unsafe { x.cast_into_unchecked() })
371 }
372
373 pub fn borrow(&self) -> Option<Ref<'_, T>> {
377 let borrow = self.untyped.borrow();
378 if borrow.is_some() {
379 Some(Ref::map(borrow, |r| unsafe {
380 r.as_ref().unwrap().as_ref().cast_into_unchecked()
381 }))
382 } else {
383 None
384 }
385 }
386
387 pub fn borrow_mut(&self) -> Option<RefMut<'_, T>> {
391 let borrow = self.untyped.borrow_mut();
392 if borrow.is_some() {
393 Some(RefMut::map(borrow, |r| unsafe {
394 r.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
395 }))
396 } else {
397 None
398 }
399 }
400
401 pub fn into_untyped(self) -> AtomicUntypedResource {
403 self.untyped
404 }
405}
406
407impl<T: HasSchema + FromWorld> AtomicResource<T> {
408 pub fn init(&self, world: &World) {
410 let mut borrow = self.untyped.borrow_mut();
411 if unlikely(borrow.is_none()) {
412 *borrow = Some(SchemaBox::new(T::from_world(world)))
413 }
414 }
415
416 #[track_caller]
418 pub fn init_borrow(&self, world: &World) -> Ref<'_, T> {
419 let map_borrow = |borrow| {
420 Ref::map(borrow, |b: &Option<SchemaBox>| unsafe {
422 b.as_ref().unwrap().as_ref().cast_into_unchecked()
423 })
424 };
425 let borrow = self.untyped.borrow();
426 if unlikely(borrow.is_none()) {
427 drop(borrow);
428 {
429 let mut borrow_mut = self.untyped.borrow_mut();
430 *borrow_mut = Some(SchemaBox::new(T::from_world(world)));
431 }
432
433 map_borrow(self.untyped.borrow())
434 } else {
435 map_borrow(borrow)
436 }
437 }
438
439 #[track_caller]
441 pub fn init_borrow_mut(&self, world: &World) -> RefMut<'_, T> {
442 let mut borrow = self.untyped.borrow_mut();
443 if unlikely(borrow.is_none()) {
444 *borrow = Some(SchemaBox::new(T::from_world(world)));
445 }
446 RefMut::map(borrow, |b| unsafe {
447 b.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
448 })
449 }
450}
451
452#[derive(Default)]
455pub struct UntypedResourceSet {
456 resources: Vec<UntypedResource>,
457}
458
459impl Clone for UntypedResourceSet {
460 fn clone(&self) -> Self {
461 Self {
462 resources: self
463 .resources
464 .iter()
465 .map(|res| {
466 if let Some(clone) = res.clone_data() {
467 UntypedResource::new(clone)
468 } else {
469 UntypedResource::empty(res.schema())
470 }
471 })
472 .collect(),
473 }
474 }
475}
476
477impl UntypedResourceSet {
478 pub fn insert_resource<T: HasSchema>(&mut self, resource: T) {
482 for r in &mut self.resources {
484 if r.schema() == T::schema() {
485 let mut borrow = r.borrow_mut();
486
487 if let Some(b) = borrow.as_mut() {
488 *b.cast_mut() = resource;
489 } else {
490 *borrow = Some(SchemaBox::new(resource))
491 }
492 return;
493 }
494 }
495
496 self.resources
498 .push(UntypedResource::new(SchemaBox::new(resource)))
499 }
500
501 pub fn init_resource<T: HasSchema + Default>(&mut self) -> RefMut<'_, T> {
504 if !self.resources.iter().any(|x| x.schema() == T::schema()) {
505 self.insert_resource(T::default());
506 }
507 self.resource_mut::<T>().unwrap()
508 }
509
510 #[track_caller]
512 pub fn resource_mut<T: HasSchema>(&self) -> Option<RefMut<'_, T>> {
513 let res = self.resources.iter().find(|x| x.schema() == T::schema())?;
514 let borrow = res.borrow_mut();
515
516 if borrow.is_some() {
517 Some(RefMut::map(borrow, |b| unsafe {
519 b.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
520 }))
521 } else {
522 None
523 }
524 }
525
526 pub fn insert_empty<T: HasSchema>(&mut self) {
529 for r in &mut self.resources {
530 if r.schema() == T::schema() {
531 let mut borrow = r.borrow_mut();
532 *borrow = None;
533 return;
534 }
535 }
536
537 self.resources.push(UntypedResource::empty(T::schema()));
539 }
540
541 pub fn resources(&self) -> &Vec<UntypedResource> {
543 &self.resources
544 }
545
546 pub fn insert_on_world(&self, world: &mut World, remove_empty: bool) {
551 for resource in self.resources.iter() {
552 let resource_cell = world.resources.untyped().get_cell(resource.schema());
553
554 if let Some(resource_copy) = resource.clone_data() {
556 resource_cell
557 .insert(resource_copy)
558 .expect("Schema mismatch error");
559 } else if remove_empty {
560 resource_cell.remove();
562 }
563 }
564 }
565}
566
567#[cfg(test)]
568mod test {
569 use std::sync::Arc;
570
571 use crate::prelude::*;
572 #[derive(HasSchema, Clone, Debug, Default)]
573 #[repr(C)]
574 struct A(String);
575
576 #[derive(HasSchema, Clone, Debug, Default)]
577 #[repr(C)]
578 struct B(u32);
579
580 #[test]
581 fn sanity_check() {
582 let r1 = Resources::new();
583
584 r1.insert(A(String::from("hi")));
585 let r1a = r1.get_cell::<A>();
586 assert_eq!(r1a.borrow().unwrap().0, "hi");
587
588 let r2 = r1.clone();
589
590 r1.insert(A(String::from("bye")));
591 r1.insert(A(String::from("world")));
592 assert_eq!(r1a.borrow().unwrap().0, "world");
593
594 let r2a = r2.get_cell::<A>();
595 assert_eq!(r2a.borrow().unwrap().0, "hi");
596
597 r1.insert(B(1));
598 let r1b = r1.get_cell::<B>();
599 assert_eq!(r1b.borrow().unwrap().0, 1);
600 r1.insert(B(2));
601 assert_eq!(r1b.borrow().unwrap().0, 2);
602 assert_eq!(r1a.borrow().unwrap().0, "world");
603 }
604
605 #[test]
606 fn resources_clear_owned_values() {
607 let mut r = Resources::new();
608
609 r.insert(A(String::from("foo")));
611
612 r.untyped
614 .insert_cell(Arc::new(UntypedResource::new(SchemaBox::new(B(1)))))
615 .unwrap();
616
617 r.clear_owned_resources();
619
620 let res_a = r.get::<A>();
622 assert!(res_a.is_none());
623
624 let res_b = r.get::<B>().unwrap();
626 assert_eq!(res_b.0, 1);
627 }
628}