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
205#[derive(Clone, Default)]
209pub struct Resources {
210 untyped: UntypedResources,
211}
212
213impl Resources {
214 pub fn new() -> Self {
216 Self::default()
217 }
218
219 pub fn insert<T: HasSchema>(&self, resource: T) -> Option<T> {
221 self.untyped
222 .get(T::schema())
223 .insert(SchemaBox::new(resource))
224 .unwrap()
225 .map(|x| x.cast_into())
226 }
227
228 pub fn contains<T: HasSchema>(&self) -> bool {
232 self.untyped.resources.contains_key(&T::schema().id())
233 }
234
235 pub fn remove<T: HasSchema>(&self) -> Option<T> {
237 self.untyped
238 .get(T::schema())
239 .remove()
240 .map(|x| unsafe { x.cast_into_unchecked() })
242 }
243
244 #[track_caller]
246 pub fn get<T: HasSchema>(&self) -> Option<Ref<T>> {
247 let b = self.untyped.get(T::schema()).borrow();
248 if b.is_some() {
249 Some(Ref::map(b, |b| unsafe {
250 b.as_ref().unwrap().as_ref().cast_into_unchecked()
251 }))
252 } else {
253 None
254 }
255 }
256
257 #[track_caller]
259 pub fn get_mut<T: HasSchema>(&self) -> Option<RefMut<T>> {
260 let b = self.untyped.get(T::schema()).borrow_mut();
261 if b.is_some() {
262 Some(RefMut::map(b, |b| unsafe {
263 b.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
264 }))
265 } else {
266 None
267 }
268 }
269
270 pub fn get_cell<T: HasSchema>(&self) -> AtomicResource<T> {
272 let untyped = self.untyped.get_cell(T::schema()).clone();
273 AtomicResource {
274 untyped,
275 _phantom: PhantomData,
276 }
277 }
278
279 pub fn untyped(&self) -> &UntypedResources {
281 &self.untyped
282 }
283
284 pub fn into_untyped(self) -> UntypedResources {
286 self.untyped
287 }
288}
289
290#[derive(Clone)]
297pub struct AtomicResource<T: HasSchema> {
298 untyped: AtomicUntypedResource,
299 _phantom: PhantomData<T>,
300}
301impl<T: HasSchema + Debug> Debug for AtomicResource<T> {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 f.write_str("AtomicResource(")?;
304 self.untyped
305 .cell
306 .borrow()
307 .as_ref()
308 .map(|x| x.cast_ref::<T>())
309 .fmt(f)?;
310 f.write_str(")")?;
311 Ok(())
312 }
313}
314
315impl<T: HasSchema + Default> Default for AtomicResource<T> {
316 fn default() -> Self {
317 Self {
318 untyped: Arc::new(UntypedResource::new(SchemaBox::new(T::default()))),
319 _phantom: Default::default(),
320 }
321 }
322}
323
324impl<T: HasSchema> AtomicResource<T> {
325 pub fn empty() -> Self {
327 Self {
328 untyped: Arc::new(UntypedResource::empty(T::schema())),
329 _phantom: PhantomData,
330 }
331 }
332
333 pub fn new(data: T) -> Self {
335 AtomicResource {
336 untyped: Arc::new(UntypedResource::new(SchemaBox::new(data))),
337 _phantom: PhantomData,
338 }
339 }
340
341 pub fn from_untyped(untyped: AtomicUntypedResource) -> Result<Self, SchemaMismatchError> {
343 T::schema().ensure_match(untyped.schema)?;
344 Ok(AtomicResource {
345 untyped,
346 _phantom: PhantomData,
347 })
348 }
349
350 pub fn remove(&self) -> Option<T> {
352 self.untyped
353 .remove()
354 .map(|x| unsafe { x.cast_into_unchecked() })
356 }
357
358 pub fn borrow(&self) -> Option<Ref<T>> {
362 let borrow = self.untyped.borrow();
363 if borrow.is_some() {
364 Some(Ref::map(borrow, |r| unsafe {
365 r.as_ref().unwrap().as_ref().cast_into_unchecked()
366 }))
367 } else {
368 None
369 }
370 }
371
372 pub fn borrow_mut(&self) -> Option<RefMut<T>> {
376 let borrow = self.untyped.borrow_mut();
377 if borrow.is_some() {
378 Some(RefMut::map(borrow, |r| unsafe {
379 r.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
380 }))
381 } else {
382 None
383 }
384 }
385
386 pub fn into_untyped(self) -> AtomicUntypedResource {
388 self.untyped
389 }
390}
391
392impl<T: HasSchema + FromWorld> AtomicResource<T> {
393 pub fn init(&self, world: &World) {
395 let mut borrow = self.untyped.borrow_mut();
396 if unlikely(borrow.is_none()) {
397 *borrow = Some(SchemaBox::new(T::from_world(world)))
398 }
399 }
400
401 #[track_caller]
403 pub fn init_borrow(&self, world: &World) -> Ref<T> {
404 let map_borrow = |borrow| {
405 Ref::map(borrow, |b: &Option<SchemaBox>| unsafe {
407 b.as_ref().unwrap().as_ref().cast_into_unchecked()
408 })
409 };
410 let borrow = self.untyped.borrow();
411 if unlikely(borrow.is_none()) {
412 drop(borrow);
413 {
414 let mut borrow_mut = self.untyped.borrow_mut();
415 *borrow_mut = Some(SchemaBox::new(T::from_world(world)));
416 }
417
418 map_borrow(self.untyped.borrow())
419 } else {
420 map_borrow(borrow)
421 }
422 }
423
424 #[track_caller]
426 pub fn init_borrow_mut(&self, world: &World) -> RefMut<T> {
427 let mut borrow = self.untyped.borrow_mut();
428 if unlikely(borrow.is_none()) {
429 *borrow = Some(SchemaBox::new(T::from_world(world)));
430 }
431 RefMut::map(borrow, |b| unsafe {
432 b.as_mut().unwrap().as_mut().cast_into_mut_unchecked()
433 })
434 }
435}
436
437#[cfg(test)]
438mod test {
439 use crate::prelude::*;
440
441 #[test]
442 fn sanity_check() {
443 #[derive(HasSchema, Clone, Debug, Default)]
444 #[repr(C)]
445 struct A(String);
446
447 #[derive(HasSchema, Clone, Debug, Default)]
448 #[repr(C)]
449 struct B(u32);
450
451 let r1 = Resources::new();
452
453 r1.insert(A(String::from("hi")));
454 let r1a = r1.get_cell::<A>();
455 assert_eq!(r1a.borrow().unwrap().0, "hi");
456
457 let r2 = r1.clone();
458
459 r1.insert(A(String::from("bye")));
460 r1.insert(A(String::from("world")));
461 assert_eq!(r1a.borrow().unwrap().0, "world");
462
463 let r2a = r2.get_cell::<A>();
464 assert_eq!(r2a.borrow().unwrap().0, "hi");
465
466 r1.insert(B(1));
467 let r1b = r1.get_cell::<B>();
468 assert_eq!(r1b.borrow().unwrap().0, 1);
469 r1.insert(B(2));
470 assert_eq!(r1b.borrow().unwrap().0, 2);
471 assert_eq!(r1a.borrow().unwrap().0, "world");
472 }
473}