goud_engine/ecs/resource.rs
1//! Resource system for the ECS.
2//!
3//! Resources are singleton data that exists outside the entity-component model.
4//! Unlike components, resources are not attached to entities - they are globally
5//! accessible within the World.
6//!
7//! # Examples of Resources
8//!
9//! - **Time**: Delta time, total elapsed time, frame count
10//! - **Input State**: Keyboard, mouse, and gamepad state
11//! - **Asset Manager**: Loaded textures, sounds, and other assets
12//! - **Configuration**: Game settings, debug flags
13//!
14//! # Resource vs Component
15//!
16//! | Aspect | Resource | Component |
17//! |--------|----------|-----------|
18//! | Cardinality | One per type | Many per type |
19//! | Ownership | Owned by World | Attached to Entity |
20//! | Access | `Res<T>`, `ResMut<T>` | `Query<&T>`, `Query<&mut T>` |
21//! | Use Case | Global state | Per-entity state |
22//!
23//! # Usage
24//!
25//! ```ignore
26//! use goud_engine::ecs::{World, Resource};
27//!
28//! // Define a resource
29//! struct Time {
30//! delta: f32,
31//! total: f32,
32//! }
33//! impl Resource for Time {}
34//!
35//! // Insert resource into World
36//! let mut world = World::new();
37//! world.insert_resource(Time { delta: 0.016, total: 0.0 });
38//!
39//! // Access resource (immutably)
40//! let time = world.get_resource::<Time>().unwrap();
41//! println!("Delta: {}", time.delta);
42//!
43//! // Access resource (mutably)
44//! let time = world.get_resource_mut::<Time>().unwrap();
45//! time.total += time.delta;
46//! ```
47//!
48//! # System Parameters
49//!
50//! In systems, use `Res<T>` and `ResMut<T>` for resource access:
51//!
52//! ```ignore
53//! fn update_system(time: Res<Time>, mut query: Query<&mut Position>) {
54//! for mut pos in query.iter_mut() {
55//! pos.x += time.delta * 10.0;
56//! }
57//! }
58//! ```
59//!
60//! # Thread Safety
61//!
62//! Resources must be `Send + Sync` for use in parallel systems. For resources
63//! that are not thread-safe (e.g., window handles), use non-send resources
64//! (future: `NonSend<T>`, `NonSendMut<T>`).
65
66use std::any::{Any, TypeId};
67use std::collections::HashMap;
68use std::fmt;
69use std::ops::{Deref, DerefMut};
70
71// =============================================================================
72// Resource Trait
73// =============================================================================
74
75/// Marker trait for types that can be used as resources.
76///
77/// Resources are singleton data stored in the [`World`](crate::ecs::World).
78/// To make a type usable as a resource, implement this trait:
79///
80/// ```ignore
81/// use goud_engine::ecs::Resource;
82///
83/// struct GameConfig {
84/// difficulty: u8,
85/// sound_volume: f32,
86/// }
87///
88/// impl Resource for GameConfig {}
89/// ```
90///
91/// # Requirements
92///
93/// Resources must be:
94/// - `Send + Sync` for parallel system execution
95/// - `'static` for type erasure in storage
96///
97/// Unlike [`Component`](crate::ecs::Component), `Resource` does NOT require
98/// explicit opt-in - any compatible type can be a resource.
99pub trait Resource: Send + Sync + 'static {}
100
101// Blanket implementation: any Send + Sync + 'static type can be a resource
102impl<T: Send + Sync + 'static> Resource for T {}
103
104// =============================================================================
105// ResourceId
106// =============================================================================
107
108/// Unique identifier for a resource type.
109///
110/// `ResourceId` wraps a `TypeId` to identify resource types at runtime.
111/// This is used internally for resource storage and access conflict detection.
112///
113/// # Example
114///
115/// ```
116/// use goud_engine::ecs::resource::ResourceId;
117///
118/// struct MyResource { value: i32 }
119///
120/// let id = ResourceId::of::<MyResource>();
121/// let id2 = ResourceId::of::<MyResource>();
122///
123/// assert_eq!(id, id2);
124/// ```
125#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
126pub struct ResourceId(TypeId);
127
128impl ResourceId {
129 /// Returns the `ResourceId` for a specific type.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// use goud_engine::ecs::resource::ResourceId;
135 ///
136 /// struct Time { delta: f32 }
137 /// let id = ResourceId::of::<Time>();
138 /// ```
139 #[inline]
140 pub fn of<T: 'static>() -> Self {
141 Self(TypeId::of::<T>())
142 }
143
144 /// Returns the underlying `TypeId`.
145 #[inline]
146 pub fn type_id(&self) -> TypeId {
147 self.0
148 }
149}
150
151impl fmt::Debug for ResourceId {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "ResourceId({:?})", self.0)
154 }
155}
156
157// =============================================================================
158// Resources Container
159// =============================================================================
160
161/// Storage container for all resources in a World.
162///
163/// `Resources` stores type-erased resource data and provides type-safe
164/// access through generic methods.
165///
166/// # Thread Safety
167///
168/// The container itself is not thread-safe. Use external synchronization
169/// or the scheduler for parallel access.
170#[derive(Default)]
171pub struct Resources {
172 /// Type-erased resource storage.
173 data: HashMap<ResourceId, Box<dyn Any + Send + Sync>>,
174}
175
176impl Resources {
177 /// Creates an empty resources container.
178 #[inline]
179 pub fn new() -> Self {
180 Self {
181 data: HashMap::new(),
182 }
183 }
184
185 /// Inserts a resource into the container.
186 ///
187 /// If a resource of this type already exists, it is replaced and the
188 /// old value is returned.
189 ///
190 /// # Arguments
191 ///
192 /// * `resource` - The resource to insert
193 ///
194 /// # Returns
195 ///
196 /// `Some(T)` if a resource of this type was replaced, `None` otherwise.
197 ///
198 /// # Example
199 ///
200 /// ```
201 /// use goud_engine::ecs::resource::Resources;
202 ///
203 /// struct Score(u32);
204 ///
205 /// let mut resources = Resources::new();
206 /// assert!(resources.insert(Score(0)).is_none());
207 /// assert!(resources.insert(Score(100)).is_some()); // Replaced
208 /// ```
209 #[inline]
210 pub fn insert<T: Resource>(&mut self, resource: T) -> Option<T> {
211 let id = ResourceId::of::<T>();
212 let old = self.data.insert(id, Box::new(resource));
213 old.and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
214 }
215
216 /// Removes a resource from the container.
217 ///
218 /// # Returns
219 ///
220 /// `Some(T)` if the resource existed, `None` otherwise.
221 ///
222 /// # Example
223 ///
224 /// ```
225 /// use goud_engine::ecs::resource::Resources;
226 ///
227 /// struct Score(u32);
228 ///
229 /// let mut resources = Resources::new();
230 /// resources.insert(Score(100));
231 ///
232 /// let score = resources.remove::<Score>();
233 /// assert!(score.is_some());
234 /// assert_eq!(score.unwrap().0, 100);
235 ///
236 /// assert!(resources.remove::<Score>().is_none()); // Already removed
237 /// ```
238 #[inline]
239 pub fn remove<T: Resource>(&mut self) -> Option<T> {
240 let id = ResourceId::of::<T>();
241 self.data
242 .remove(&id)
243 .and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
244 }
245
246 /// Returns an immutable reference to a resource.
247 ///
248 /// # Returns
249 ///
250 /// `Some(&T)` if the resource exists, `None` otherwise.
251 ///
252 /// # Example
253 ///
254 /// ```
255 /// use goud_engine::ecs::resource::Resources;
256 ///
257 /// struct Score(u32);
258 ///
259 /// let mut resources = Resources::new();
260 /// resources.insert(Score(100));
261 ///
262 /// let score = resources.get::<Score>().unwrap();
263 /// assert_eq!(score.0, 100);
264 /// ```
265 #[inline]
266 pub fn get<T: Resource>(&self) -> Option<&T> {
267 let id = ResourceId::of::<T>();
268 self.data.get(&id).and_then(|boxed| boxed.downcast_ref())
269 }
270
271 /// Returns a mutable reference to a resource.
272 ///
273 /// # Returns
274 ///
275 /// `Some(&mut T)` if the resource exists, `None` otherwise.
276 ///
277 /// # Example
278 ///
279 /// ```
280 /// use goud_engine::ecs::resource::Resources;
281 ///
282 /// struct Score(u32);
283 ///
284 /// let mut resources = Resources::new();
285 /// resources.insert(Score(100));
286 ///
287 /// let score = resources.get_mut::<Score>().unwrap();
288 /// score.0 += 50;
289 /// assert_eq!(resources.get::<Score>().unwrap().0, 150);
290 /// ```
291 #[inline]
292 pub fn get_mut<T: Resource>(&mut self) -> Option<&mut T> {
293 let id = ResourceId::of::<T>();
294 self.data
295 .get_mut(&id)
296 .and_then(|boxed| boxed.downcast_mut())
297 }
298
299 /// Returns `true` if a resource of the specified type exists.
300 ///
301 /// # Example
302 ///
303 /// ```
304 /// use goud_engine::ecs::resource::Resources;
305 ///
306 /// struct Score(u32);
307 /// struct Health(f32);
308 ///
309 /// let mut resources = Resources::new();
310 /// resources.insert(Score(100));
311 ///
312 /// assert!(resources.contains::<Score>());
313 /// assert!(!resources.contains::<Health>());
314 /// ```
315 #[inline]
316 pub fn contains<T: Resource>(&self) -> bool {
317 self.data.contains_key(&ResourceId::of::<T>())
318 }
319
320 /// Returns the number of resources in the container.
321 #[inline]
322 pub fn len(&self) -> usize {
323 self.data.len()
324 }
325
326 /// Returns `true` if there are no resources.
327 #[inline]
328 pub fn is_empty(&self) -> bool {
329 self.data.is_empty()
330 }
331
332 /// Removes all resources from the container.
333 #[inline]
334 pub fn clear(&mut self) {
335 self.data.clear();
336 }
337}
338
339impl fmt::Debug for Resources {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 f.debug_struct("Resources")
342 .field("count", &self.data.len())
343 .finish()
344 }
345}
346
347// =============================================================================
348// Res<T> - Immutable Resource Access
349// =============================================================================
350
351/// Immutable access to a resource of type `T`.
352///
353/// `Res<T>` provides read-only access to a resource stored in the World.
354/// It implements `Deref`, so you can access the inner value directly.
355///
356/// # Panics
357///
358/// Operations on `Res<T>` will panic if the resource doesn't exist.
359/// Use `Option<Res<T>>` for optional access (future).
360///
361/// # Example
362///
363/// ```ignore
364/// fn print_time(time: Res<Time>) {
365/// println!("Delta: {}, Total: {}", time.delta, time.total);
366/// }
367/// ```
368///
369/// # Thread Safety
370///
371/// Multiple `Res<T>` instances can coexist, as they only provide read access.
372/// They conflict with `ResMut<T>` on the same resource type.
373pub struct Res<'w, T: Resource> {
374 value: &'w T,
375}
376
377impl<'w, T: Resource> Res<'w, T> {
378 /// Creates a new `Res` from a reference.
379 ///
380 /// This is primarily used internally by the system parameter infrastructure.
381 #[inline]
382 pub fn new(value: &'w T) -> Self {
383 Self { value }
384 }
385
386 /// Returns a reference to the inner value.
387 #[inline]
388 pub fn into_inner(self) -> &'w T {
389 self.value
390 }
391}
392
393impl<T: Resource> Deref for Res<'_, T> {
394 type Target = T;
395
396 #[inline]
397 fn deref(&self) -> &Self::Target {
398 self.value
399 }
400}
401
402impl<T: Resource + fmt::Debug> fmt::Debug for Res<'_, T> {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 f.debug_tuple("Res").field(&self.value).finish()
405 }
406}
407
408// Clone for Res - it's just a reference, so this is cheap
409impl<T: Resource> Clone for Res<'_, T> {
410 #[inline]
411 fn clone(&self) -> Self {
412 *self
413 }
414}
415
416impl<T: Resource> Copy for Res<'_, T> {}
417
418// =============================================================================
419// ResMut<T> - Mutable Resource Access
420// =============================================================================
421
422/// Mutable access to a resource of type `T`.
423///
424/// `ResMut<T>` provides read-write access to a resource stored in the World.
425/// It implements `Deref` and `DerefMut`, so you can access the inner value directly.
426///
427/// # Panics
428///
429/// Operations on `ResMut<T>` will panic if the resource doesn't exist.
430/// Use `Option<ResMut<T>>` for optional access (future).
431///
432/// # Example
433///
434/// ```ignore
435/// fn update_time(mut time: ResMut<Time>, delta: f32) {
436/// time.delta = delta;
437/// time.total += delta;
438/// }
439/// ```
440///
441/// # Thread Safety
442///
443/// Only one `ResMut<T>` can exist at a time for a given resource type.
444/// It conflicts with both `Res<T>` and `ResMut<T>` on the same type.
445pub struct ResMut<'w, T: Resource> {
446 value: &'w mut T,
447}
448
449impl<'w, T: Resource> ResMut<'w, T> {
450 /// Creates a new `ResMut` from a mutable reference.
451 ///
452 /// This is primarily used internally by the system parameter infrastructure.
453 #[inline]
454 pub fn new(value: &'w mut T) -> Self {
455 Self { value }
456 }
457
458 /// Returns a mutable reference to the inner value.
459 #[inline]
460 pub fn into_inner(self) -> &'w mut T {
461 self.value
462 }
463}
464
465impl<T: Resource> Deref for ResMut<'_, T> {
466 type Target = T;
467
468 #[inline]
469 fn deref(&self) -> &Self::Target {
470 self.value
471 }
472}
473
474impl<T: Resource> DerefMut for ResMut<'_, T> {
475 #[inline]
476 fn deref_mut(&mut self) -> &mut Self::Target {
477 self.value
478 }
479}
480
481impl<T: Resource + fmt::Debug> fmt::Debug for ResMut<'_, T> {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 f.debug_tuple("ResMut").field(&self.value).finish()
484 }
485}
486
487// =============================================================================
488// Non-Send Resources
489// =============================================================================
490
491/// Marker trait for types that can be used as non-send resources.
492///
493/// Non-send resources are resources that cannot be safely sent between threads.
494/// Unlike regular [`Resource`], non-send resources only require `'static`.
495///
496/// # Use Cases
497///
498/// Non-send resources are useful for:
499///
500/// - **Raw pointers**: Window handles, OpenGL contexts, etc.
501/// - **Thread-local data**: Data bound to a specific thread
502/// - **Rc/RefCell types**: Reference-counted non-atomic types
503/// - **Platform-specific handles**: OS handles that are thread-affine
504///
505/// # Example
506///
507/// ```
508/// use goud_engine::ecs::resource::NonSendResource;
509/// use std::rc::Rc;
510/// use std::cell::RefCell;
511///
512/// // This type is NOT Send because of Rc
513/// struct WindowHandle {
514/// id: Rc<RefCell<u32>>,
515/// }
516///
517/// // Explicit implementation required
518/// impl NonSendResource for WindowHandle {}
519/// ```
520///
521/// # Thread Safety
522///
523/// Non-send resources must only be accessed from the main thread.
524/// The scheduler ensures non-send systems run on the main thread.
525///
526/// # Differences from Resource
527///
528/// | Aspect | Resource | NonSendResource |
529/// |--------|----------|-----------------|
530/// | Thread-safe | Yes (Send + Sync) | No |
531/// | System access | Any thread | Main thread only |
532/// | Storage | `Resources` | `NonSendResources` |
533/// | Wrapper | `Res<T>`, `ResMut<T>` | `NonSend<T>`, `NonSendMut<T>` |
534pub trait NonSendResource: 'static {}
535
536// NOTE: No blanket implementation - types must explicitly opt-in to non-send
537// resources. This is intentional to prevent accidentally using non-thread-safe
538// types as regular resources.
539
540// =============================================================================
541// NonSendResourceId
542// =============================================================================
543
544/// Unique identifier for a non-send resource type.
545///
546/// Like [`ResourceId`] but for non-send resources. Used internally
547/// for storage and access conflict detection.
548#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
549pub struct NonSendResourceId(TypeId);
550
551impl NonSendResourceId {
552 /// Returns the `NonSendResourceId` for a specific type.
553 #[inline]
554 pub fn of<T: 'static>() -> Self {
555 Self(TypeId::of::<T>())
556 }
557
558 /// Returns the underlying `TypeId`.
559 #[inline]
560 pub fn type_id(&self) -> TypeId {
561 self.0
562 }
563}
564
565impl fmt::Debug for NonSendResourceId {
566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567 write!(f, "NonSendResourceId({:?})", self.0)
568 }
569}
570
571// =============================================================================
572// NonSendResources Container
573// =============================================================================
574
575/// Marker type that prevents a struct from implementing `Send` or `Sync`.
576///
577/// This is used to ensure `NonSendResources` cannot be sent between threads.
578/// The `*const ()` raw pointer makes the containing type `!Send` and `!Sync`.
579pub struct NonSendMarker(std::marker::PhantomData<*const ()>);
580
581// Safety: NonSendMarker is intentionally NOT Send or Sync.
582// This is a marker type used to "infect" containing types.
583
584impl Default for NonSendMarker {
585 fn default() -> Self {
586 Self(std::marker::PhantomData)
587 }
588}
589
590impl fmt::Debug for NonSendMarker {
591 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592 write!(f, "NonSendMarker")
593 }
594}
595
596/// Storage container for non-send resources.
597///
598/// Unlike [`Resources`], this container stores resources that are NOT `Send + Sync`.
599/// These resources must only be accessed from the thread they were created on
600/// (typically the main thread).
601///
602/// # Thread Safety
603///
604/// This container is NOT `Send` or `Sync`. It must remain on the main thread.
605/// The scheduler ensures that systems using non-send resources run on the main thread.
606///
607/// # Example
608///
609/// ```
610/// use goud_engine::ecs::resource::{NonSendResources, NonSendResource};
611/// use std::rc::Rc;
612///
613/// struct RawPointerResource {
614/// ptr: *mut u32,
615/// }
616/// impl NonSendResource for RawPointerResource {}
617///
618/// let mut resources = NonSendResources::new();
619///
620/// let mut value = 42u32;
621/// resources.insert(RawPointerResource { ptr: &mut value as *mut u32 });
622///
623/// assert!(resources.contains::<RawPointerResource>());
624/// ```
625#[derive(Default)]
626pub struct NonSendResources {
627 /// Type-erased non-send resource storage.
628 /// Note: Uses `Box<dyn Any>` NOT `Box<dyn Any + Send + Sync>`.
629 data: HashMap<NonSendResourceId, Box<dyn Any>>,
630 /// Marker to make this type !Send and !Sync.
631 _marker: NonSendMarker,
632}
633
634impl NonSendResources {
635 /// Creates an empty non-send resources container.
636 #[inline]
637 pub fn new() -> Self {
638 Self {
639 data: HashMap::new(),
640 _marker: NonSendMarker::default(),
641 }
642 }
643
644 /// Inserts a non-send resource into the container.
645 ///
646 /// If a resource of this type already exists, it is replaced and the
647 /// old value is returned.
648 ///
649 /// # Arguments
650 ///
651 /// * `resource` - The resource to insert
652 ///
653 /// # Returns
654 ///
655 /// `Some(T)` if a resource of this type was replaced, `None` otherwise.
656 #[inline]
657 pub fn insert<T: NonSendResource>(&mut self, resource: T) -> Option<T> {
658 let id = NonSendResourceId::of::<T>();
659 let old = self.data.insert(id, Box::new(resource));
660 old.and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
661 }
662
663 /// Removes a non-send resource from the container.
664 ///
665 /// # Returns
666 ///
667 /// `Some(T)` if the resource existed, `None` otherwise.
668 #[inline]
669 pub fn remove<T: NonSendResource>(&mut self) -> Option<T> {
670 let id = NonSendResourceId::of::<T>();
671 self.data
672 .remove(&id)
673 .and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
674 }
675
676 /// Returns an immutable reference to a non-send resource.
677 ///
678 /// # Returns
679 ///
680 /// `Some(&T)` if the resource exists, `None` otherwise.
681 #[inline]
682 pub fn get<T: NonSendResource>(&self) -> Option<&T> {
683 let id = NonSendResourceId::of::<T>();
684 self.data.get(&id).and_then(|boxed| boxed.downcast_ref())
685 }
686
687 /// Returns a mutable reference to a non-send resource.
688 ///
689 /// # Returns
690 ///
691 /// `Some(&mut T)` if the resource exists, `None` otherwise.
692 #[inline]
693 pub fn get_mut<T: NonSendResource>(&mut self) -> Option<&mut T> {
694 let id = NonSendResourceId::of::<T>();
695 self.data
696 .get_mut(&id)
697 .and_then(|boxed| boxed.downcast_mut())
698 }
699
700 /// Returns `true` if a non-send resource of the specified type exists.
701 #[inline]
702 pub fn contains<T: NonSendResource>(&self) -> bool {
703 self.data.contains_key(&NonSendResourceId::of::<T>())
704 }
705
706 /// Returns the number of non-send resources in the container.
707 #[inline]
708 pub fn len(&self) -> usize {
709 self.data.len()
710 }
711
712 /// Returns `true` if there are no non-send resources.
713 #[inline]
714 pub fn is_empty(&self) -> bool {
715 self.data.is_empty()
716 }
717
718 /// Removes all non-send resources from the container.
719 #[inline]
720 pub fn clear(&mut self) {
721 self.data.clear();
722 }
723}
724
725impl fmt::Debug for NonSendResources {
726 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
727 f.debug_struct("NonSendResources")
728 .field("count", &self.data.len())
729 .finish()
730 }
731}
732
733// NonSendResources is NOT Send or Sync due to NonSendMarker containing *const ()
734// This is verified by compile-time tests in the tests module.
735
736// =============================================================================
737// NonSend<T> - Immutable Non-Send Resource Access
738// =============================================================================
739
740/// Immutable access to a non-send resource of type `T`.
741///
742/// `NonSend<T>` provides read-only access to a non-send resource stored in the World.
743/// It implements `Deref`, so you can access the inner value directly.
744///
745/// # Thread Safety
746///
747/// Systems using `NonSend<T>` are constrained to run on the main thread.
748/// This is enforced by the scheduler at runtime.
749///
750/// # Example
751///
752/// ```ignore
753/// fn print_window_info(window: NonSend<WindowHandle>) {
754/// println!("Window handle: {:?}", window.id);
755/// }
756/// ```
757///
758/// # Panics
759///
760/// Operations on `NonSend<T>` will panic if the resource doesn't exist.
761/// Use `Option<NonSend<T>>` for optional access.
762pub struct NonSend<'w, T: NonSendResource> {
763 value: &'w T,
764}
765
766impl<'w, T: NonSendResource> NonSend<'w, T> {
767 /// Creates a new `NonSend` from a reference.
768 #[inline]
769 pub fn new(value: &'w T) -> Self {
770 Self { value }
771 }
772
773 /// Returns a reference to the inner value.
774 #[inline]
775 pub fn into_inner(self) -> &'w T {
776 self.value
777 }
778}
779
780impl<T: NonSendResource> Deref for NonSend<'_, T> {
781 type Target = T;
782
783 #[inline]
784 fn deref(&self) -> &Self::Target {
785 self.value
786 }
787}
788
789impl<T: NonSendResource + fmt::Debug> fmt::Debug for NonSend<'_, T> {
790 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
791 f.debug_tuple("NonSend").field(&self.value).finish()
792 }
793}
794
795// Clone for NonSend - it's just a reference, so this is cheap
796impl<T: NonSendResource> Clone for NonSend<'_, T> {
797 #[inline]
798 fn clone(&self) -> Self {
799 *self
800 }
801}
802
803impl<T: NonSendResource> Copy for NonSend<'_, T> {}
804
805// =============================================================================
806// NonSendMut<T> - Mutable Non-Send Resource Access
807// =============================================================================
808
809/// Mutable access to a non-send resource of type `T`.
810///
811/// `NonSendMut<T>` provides read-write access to a non-send resource stored in the World.
812/// It implements `Deref` and `DerefMut`, so you can access the inner value directly.
813///
814/// # Thread Safety
815///
816/// Systems using `NonSendMut<T>` are constrained to run on the main thread.
817/// This is enforced by the scheduler at runtime.
818///
819/// # Example
820///
821/// ```ignore
822/// fn update_window(mut window: NonSendMut<WindowHandle>) {
823/// window.update_title("New Title");
824/// }
825/// ```
826///
827/// # Panics
828///
829/// Operations on `NonSendMut<T>` will panic if the resource doesn't exist.
830/// Use `Option<NonSendMut<T>>` for optional access.
831pub struct NonSendMut<'w, T: NonSendResource> {
832 value: &'w mut T,
833}
834
835impl<'w, T: NonSendResource> NonSendMut<'w, T> {
836 /// Creates a new `NonSendMut` from a mutable reference.
837 #[inline]
838 pub fn new(value: &'w mut T) -> Self {
839 Self { value }
840 }
841
842 /// Returns a mutable reference to the inner value.
843 #[inline]
844 pub fn into_inner(self) -> &'w mut T {
845 self.value
846 }
847}
848
849impl<T: NonSendResource> Deref for NonSendMut<'_, T> {
850 type Target = T;
851
852 #[inline]
853 fn deref(&self) -> &Self::Target {
854 self.value
855 }
856}
857
858impl<T: NonSendResource> DerefMut for NonSendMut<'_, T> {
859 #[inline]
860 fn deref_mut(&mut self) -> &mut Self::Target {
861 self.value
862 }
863}
864
865impl<T: NonSendResource + fmt::Debug> fmt::Debug for NonSendMut<'_, T> {
866 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
867 f.debug_tuple("NonSendMut").field(&self.value).finish()
868 }
869}
870
871// =============================================================================
872// Tests
873// =============================================================================
874
875#[cfg(test)]
876mod tests {
877 use super::*;
878
879 // Test resources
880 #[derive(Debug)]
881 struct Time {
882 delta: f32,
883 total: f32,
884 }
885
886 #[derive(Debug)]
887 struct Score(u32);
888
889 #[derive(Debug)]
890 struct Config {
891 debug: bool,
892 volume: f32,
893 }
894
895 // =========================================================================
896 // Resource Trait Tests
897 // =========================================================================
898
899 mod resource_trait {
900 use super::*;
901
902 #[test]
903 fn test_resource_auto_impl() {
904 // Any Send + Sync + 'static type should be a Resource
905 fn requires_resource<T: Resource>() {}
906
907 requires_resource::<Time>();
908 requires_resource::<Score>();
909 requires_resource::<i32>();
910 requires_resource::<String>();
911 requires_resource::<Vec<u8>>();
912 }
913
914 #[test]
915 fn test_resource_is_send() {
916 fn requires_send<T: Send>() {}
917 requires_send::<Time>();
918 requires_send::<Score>();
919 }
920
921 #[test]
922 fn test_resource_is_sync() {
923 fn requires_sync<T: Sync>() {}
924 requires_sync::<Time>();
925 requires_sync::<Score>();
926 }
927 }
928
929 // =========================================================================
930 // ResourceId Tests
931 // =========================================================================
932
933 mod resource_id {
934 use super::*;
935
936 #[test]
937 fn test_resource_id_of() {
938 let id1 = ResourceId::of::<Time>();
939 let id2 = ResourceId::of::<Time>();
940 assert_eq!(id1, id2);
941 }
942
943 #[test]
944 fn test_resource_id_different_types() {
945 let id1 = ResourceId::of::<Time>();
946 let id2 = ResourceId::of::<Score>();
947 assert_ne!(id1, id2);
948 }
949
950 #[test]
951 fn test_resource_id_type_id() {
952 let id = ResourceId::of::<Time>();
953 assert_eq!(id.type_id(), TypeId::of::<Time>());
954 }
955
956 #[test]
957 fn test_resource_id_hash() {
958 use std::collections::HashSet;
959 let mut set = HashSet::new();
960 set.insert(ResourceId::of::<Time>());
961 set.insert(ResourceId::of::<Score>());
962 assert_eq!(set.len(), 2);
963
964 // Same type should not add again
965 set.insert(ResourceId::of::<Time>());
966 assert_eq!(set.len(), 2);
967 }
968
969 #[test]
970 fn test_resource_id_ord() {
971 use std::collections::BTreeSet;
972 let mut set = BTreeSet::new();
973 set.insert(ResourceId::of::<Time>());
974 set.insert(ResourceId::of::<Score>());
975 assert_eq!(set.len(), 2);
976 }
977
978 #[test]
979 fn test_resource_id_debug() {
980 let id = ResourceId::of::<Time>();
981 let debug_str = format!("{:?}", id);
982 assert!(debug_str.contains("ResourceId"));
983 }
984
985 #[test]
986 fn test_resource_id_clone() {
987 let id1 = ResourceId::of::<Time>();
988 let id2 = id1;
989 assert_eq!(id1, id2);
990 }
991 }
992
993 // =========================================================================
994 // Resources Container Tests
995 // =========================================================================
996
997 mod resources_container {
998 use super::*;
999
1000 #[test]
1001 fn test_resources_new() {
1002 let resources = Resources::new();
1003 assert!(resources.is_empty());
1004 assert_eq!(resources.len(), 0);
1005 }
1006
1007 #[test]
1008 fn test_resources_default() {
1009 let resources = Resources::default();
1010 assert!(resources.is_empty());
1011 }
1012
1013 #[test]
1014 fn test_resources_insert() {
1015 let mut resources = Resources::new();
1016 let old = resources.insert(Score(100));
1017 assert!(old.is_none());
1018 assert_eq!(resources.len(), 1);
1019 }
1020
1021 #[test]
1022 fn test_resources_insert_replace() {
1023 let mut resources = Resources::new();
1024 resources.insert(Score(100));
1025 let old = resources.insert(Score(200));
1026 assert!(old.is_some());
1027 assert_eq!(old.unwrap().0, 100);
1028 assert_eq!(resources.get::<Score>().unwrap().0, 200);
1029 }
1030
1031 #[test]
1032 fn test_resources_remove() {
1033 let mut resources = Resources::new();
1034 resources.insert(Score(100));
1035
1036 let removed = resources.remove::<Score>();
1037 assert!(removed.is_some());
1038 assert_eq!(removed.unwrap().0, 100);
1039 assert!(resources.is_empty());
1040 }
1041
1042 #[test]
1043 fn test_resources_remove_nonexistent() {
1044 let mut resources = Resources::new();
1045 let removed = resources.remove::<Score>();
1046 assert!(removed.is_none());
1047 }
1048
1049 #[test]
1050 fn test_resources_get() {
1051 let mut resources = Resources::new();
1052 resources.insert(Score(100));
1053
1054 let score = resources.get::<Score>();
1055 assert!(score.is_some());
1056 assert_eq!(score.unwrap().0, 100);
1057 }
1058
1059 #[test]
1060 fn test_resources_get_nonexistent() {
1061 let resources = Resources::new();
1062 assert!(resources.get::<Score>().is_none());
1063 }
1064
1065 #[test]
1066 fn test_resources_get_mut() {
1067 let mut resources = Resources::new();
1068 resources.insert(Score(100));
1069
1070 let score = resources.get_mut::<Score>().unwrap();
1071 score.0 += 50;
1072
1073 assert_eq!(resources.get::<Score>().unwrap().0, 150);
1074 }
1075
1076 #[test]
1077 fn test_resources_get_mut_nonexistent() {
1078 let mut resources = Resources::new();
1079 assert!(resources.get_mut::<Score>().is_none());
1080 }
1081
1082 #[test]
1083 fn test_resources_contains() {
1084 let mut resources = Resources::new();
1085 assert!(!resources.contains::<Score>());
1086
1087 resources.insert(Score(100));
1088 assert!(resources.contains::<Score>());
1089
1090 resources.remove::<Score>();
1091 assert!(!resources.contains::<Score>());
1092 }
1093
1094 #[test]
1095 fn test_resources_multiple_types() {
1096 let mut resources = Resources::new();
1097 resources.insert(Score(100));
1098 resources.insert(Time {
1099 delta: 0.016,
1100 total: 0.0,
1101 });
1102 resources.insert(Config {
1103 debug: true,
1104 volume: 0.8,
1105 });
1106
1107 assert_eq!(resources.len(), 3);
1108 assert_eq!(resources.get::<Score>().unwrap().0, 100);
1109 assert_eq!(resources.get::<Time>().unwrap().delta, 0.016);
1110 assert!(resources.get::<Config>().unwrap().debug);
1111 }
1112
1113 #[test]
1114 fn test_resources_clear() {
1115 let mut resources = Resources::new();
1116 resources.insert(Score(100));
1117 resources.insert(Time {
1118 delta: 0.016,
1119 total: 0.0,
1120 });
1121
1122 resources.clear();
1123 assert!(resources.is_empty());
1124 assert_eq!(resources.len(), 0);
1125 }
1126
1127 #[test]
1128 fn test_resources_debug() {
1129 let mut resources = Resources::new();
1130 resources.insert(Score(100));
1131
1132 let debug_str = format!("{:?}", resources);
1133 assert!(debug_str.contains("Resources"));
1134 assert!(debug_str.contains("count"));
1135 }
1136 }
1137
1138 // =========================================================================
1139 // Res<T> Tests
1140 // =========================================================================
1141
1142 mod res_tests {
1143 use super::*;
1144
1145 #[test]
1146 fn test_res_new() {
1147 let time = Time {
1148 delta: 0.016,
1149 total: 1.0,
1150 };
1151 let res = Res::new(&time);
1152 assert_eq!(res.delta, 0.016);
1153 assert_eq!(res.total, 1.0);
1154 }
1155
1156 #[test]
1157 fn test_res_deref() {
1158 let score = Score(100);
1159 let res = Res::new(&score);
1160 assert_eq!(res.0, 100);
1161 }
1162
1163 #[test]
1164 fn test_res_into_inner() {
1165 let score = Score(100);
1166 let res = Res::new(&score);
1167 let inner = res.into_inner();
1168 assert_eq!(inner.0, 100);
1169 }
1170
1171 #[test]
1172 fn test_res_debug() {
1173 let score = Score(100);
1174 let res = Res::new(&score);
1175 let debug_str = format!("{:?}", res);
1176 assert!(debug_str.contains("Res"));
1177 }
1178
1179 #[test]
1180 fn test_res_clone() {
1181 let score = Score(100);
1182 let res = Res::new(&score);
1183 let cloned = res;
1184 assert_eq!(cloned.0, 100);
1185 }
1186
1187 #[test]
1188 fn test_res_copy() {
1189 let score = Score(100);
1190 let res = Res::new(&score);
1191 let copied = res;
1192 // Both still valid
1193 assert_eq!(res.0, 100);
1194 assert_eq!(copied.0, 100);
1195 }
1196 }
1197
1198 // =========================================================================
1199 // ResMut<T> Tests
1200 // =========================================================================
1201
1202 mod res_mut_tests {
1203 use super::*;
1204
1205 #[test]
1206 fn test_res_mut_new() {
1207 let mut time = Time {
1208 delta: 0.016,
1209 total: 1.0,
1210 };
1211 let res = ResMut::new(&mut time);
1212 assert_eq!(res.delta, 0.016);
1213 assert_eq!(res.total, 1.0);
1214 }
1215
1216 #[test]
1217 fn test_res_mut_deref() {
1218 let mut score = Score(100);
1219 let res = ResMut::new(&mut score);
1220 assert_eq!(res.0, 100);
1221 }
1222
1223 #[test]
1224 fn test_res_mut_deref_mut() {
1225 let mut score = Score(100);
1226 {
1227 let mut res = ResMut::new(&mut score);
1228 res.0 += 50;
1229 }
1230 assert_eq!(score.0, 150);
1231 }
1232
1233 #[test]
1234 fn test_res_mut_into_inner() {
1235 let mut score = Score(100);
1236 let res = ResMut::new(&mut score);
1237 let inner = res.into_inner();
1238 inner.0 += 50;
1239 assert_eq!(score.0, 150);
1240 }
1241
1242 #[test]
1243 fn test_res_mut_debug() {
1244 let mut score = Score(100);
1245 let res = ResMut::new(&mut score);
1246 let debug_str = format!("{:?}", res);
1247 assert!(debug_str.contains("ResMut"));
1248 }
1249
1250 #[test]
1251 fn test_res_mut_modify_complex() {
1252 let mut time = Time {
1253 delta: 0.016,
1254 total: 0.0,
1255 };
1256
1257 {
1258 let mut res = ResMut::new(&mut time);
1259 res.total += res.delta;
1260 }
1261
1262 assert_eq!(time.total, 0.016);
1263 }
1264 }
1265
1266 // =========================================================================
1267 // Integration Tests
1268 // =========================================================================
1269
1270 mod integration {
1271 use super::*;
1272
1273 #[test]
1274 fn test_resources_with_res() {
1275 let mut resources = Resources::new();
1276 resources.insert(Score(100));
1277
1278 let score_ref = resources.get::<Score>().unwrap();
1279 let res = Res::new(score_ref);
1280
1281 assert_eq!(res.0, 100);
1282 }
1283
1284 #[test]
1285 fn test_resources_with_res_mut() {
1286 let mut resources = Resources::new();
1287 resources.insert(Score(100));
1288
1289 {
1290 let score_ref = resources.get_mut::<Score>().unwrap();
1291 let mut res = ResMut::new(score_ref);
1292 res.0 += 50;
1293 }
1294
1295 assert_eq!(resources.get::<Score>().unwrap().0, 150);
1296 }
1297
1298 #[test]
1299 fn test_resource_lifecycle() {
1300 let mut resources = Resources::new();
1301
1302 // Insert
1303 resources.insert(Score(0));
1304 assert!(resources.contains::<Score>());
1305
1306 // Modify
1307 resources.get_mut::<Score>().unwrap().0 = 100;
1308
1309 // Read
1310 assert_eq!(resources.get::<Score>().unwrap().0, 100);
1311
1312 // Replace
1313 resources.insert(Score(200));
1314 assert_eq!(resources.get::<Score>().unwrap().0, 200);
1315
1316 // Remove
1317 let removed = resources.remove::<Score>();
1318 assert_eq!(removed.unwrap().0, 200);
1319 assert!(!resources.contains::<Score>());
1320 }
1321 }
1322
1323 // =========================================================================
1324 // Thread Safety Tests
1325 // =========================================================================
1326
1327 mod thread_safety {
1328 use super::*;
1329
1330 #[test]
1331 fn test_resources_is_send() {
1332 fn requires_send<T: Send>() {}
1333 requires_send::<Resources>();
1334 }
1335
1336 #[test]
1337 fn test_res_is_send() {
1338 fn requires_send<T: Send>() {}
1339 // Res is Send if T is Send
1340 fn check<T: Resource>() {
1341 // Can't directly check Res<'_, T> for Send due to lifetime
1342 // but the underlying reference is Send if T is Sync
1343 }
1344 check::<Score>();
1345 }
1346
1347 #[test]
1348 fn test_res_mut_is_send() {
1349 fn requires_send<T: Send>() {}
1350 // ResMut is Send if T is Send
1351 fn check<T: Resource>() {
1352 // Can't directly check ResMut<'_, T> for Send due to lifetime
1353 }
1354 check::<Score>();
1355 }
1356 }
1357
1358 // =========================================================================
1359 // Non-Send Resource Tests
1360 // =========================================================================
1361
1362 mod non_send_resources {
1363 use super::*;
1364 use std::cell::RefCell;
1365 use std::rc::Rc;
1366
1367 // Non-send test resources
1368 struct WindowHandle {
1369 id: Rc<u32>,
1370 }
1371 impl NonSendResource for WindowHandle {}
1372
1373 struct OpenGLContext {
1374 ctx: Rc<RefCell<u32>>,
1375 }
1376 impl NonSendResource for OpenGLContext {}
1377
1378 struct RawPointerResource {
1379 ptr: *mut u32,
1380 }
1381 impl NonSendResource for RawPointerResource {}
1382
1383 // =====================================================================
1384 // NonSendResourceId Tests
1385 // =====================================================================
1386
1387 #[test]
1388 fn test_non_send_resource_id_of() {
1389 let id1 = NonSendResourceId::of::<WindowHandle>();
1390 let id2 = NonSendResourceId::of::<WindowHandle>();
1391 assert_eq!(id1, id2);
1392 }
1393
1394 #[test]
1395 fn test_non_send_resource_id_different_types() {
1396 let id1 = NonSendResourceId::of::<WindowHandle>();
1397 let id2 = NonSendResourceId::of::<OpenGLContext>();
1398 assert_ne!(id1, id2);
1399 }
1400
1401 #[test]
1402 fn test_non_send_resource_id_type_id() {
1403 let id = NonSendResourceId::of::<WindowHandle>();
1404 assert_eq!(id.type_id(), TypeId::of::<WindowHandle>());
1405 }
1406
1407 #[test]
1408 fn test_non_send_resource_id_hash() {
1409 use std::collections::HashSet;
1410 let mut set = HashSet::new();
1411 set.insert(NonSendResourceId::of::<WindowHandle>());
1412 set.insert(NonSendResourceId::of::<OpenGLContext>());
1413 assert_eq!(set.len(), 2);
1414 }
1415
1416 #[test]
1417 fn test_non_send_resource_id_ord() {
1418 use std::collections::BTreeSet;
1419 let mut set = BTreeSet::new();
1420 set.insert(NonSendResourceId::of::<WindowHandle>());
1421 set.insert(NonSendResourceId::of::<OpenGLContext>());
1422 assert_eq!(set.len(), 2);
1423 }
1424
1425 #[test]
1426 fn test_non_send_resource_id_debug() {
1427 let id = NonSendResourceId::of::<WindowHandle>();
1428 let debug_str = format!("{:?}", id);
1429 assert!(debug_str.contains("NonSendResourceId"));
1430 }
1431
1432 // =====================================================================
1433 // NonSendResources Container Tests
1434 // =====================================================================
1435
1436 #[test]
1437 fn test_non_send_resources_new() {
1438 let resources = NonSendResources::new();
1439 assert!(resources.is_empty());
1440 assert_eq!(resources.len(), 0);
1441 }
1442
1443 #[test]
1444 fn test_non_send_resources_default() {
1445 let resources = NonSendResources::default();
1446 assert!(resources.is_empty());
1447 }
1448
1449 #[test]
1450 fn test_non_send_resources_insert() {
1451 let mut resources = NonSendResources::new();
1452 let old = resources.insert(WindowHandle { id: Rc::new(42) });
1453 assert!(old.is_none());
1454 assert_eq!(resources.len(), 1);
1455 }
1456
1457 #[test]
1458 fn test_non_send_resources_insert_replace() {
1459 let mut resources = NonSendResources::new();
1460 resources.insert(WindowHandle { id: Rc::new(42) });
1461 let old = resources.insert(WindowHandle { id: Rc::new(100) });
1462 assert!(old.is_some());
1463 assert_eq!(*old.unwrap().id, 42);
1464 assert_eq!(*resources.get::<WindowHandle>().unwrap().id, 100);
1465 }
1466
1467 #[test]
1468 fn test_non_send_resources_remove() {
1469 let mut resources = NonSendResources::new();
1470 resources.insert(WindowHandle { id: Rc::new(42) });
1471
1472 let removed = resources.remove::<WindowHandle>();
1473 assert!(removed.is_some());
1474 assert_eq!(*removed.unwrap().id, 42);
1475 assert!(resources.is_empty());
1476 }
1477
1478 #[test]
1479 fn test_non_send_resources_remove_nonexistent() {
1480 let mut resources = NonSendResources::new();
1481 let removed = resources.remove::<WindowHandle>();
1482 assert!(removed.is_none());
1483 }
1484
1485 #[test]
1486 fn test_non_send_resources_get() {
1487 let mut resources = NonSendResources::new();
1488 resources.insert(WindowHandle { id: Rc::new(42) });
1489
1490 let handle = resources.get::<WindowHandle>();
1491 assert!(handle.is_some());
1492 assert_eq!(*handle.unwrap().id, 42);
1493 }
1494
1495 #[test]
1496 fn test_non_send_resources_get_nonexistent() {
1497 let resources = NonSendResources::new();
1498 assert!(resources.get::<WindowHandle>().is_none());
1499 }
1500
1501 #[test]
1502 fn test_non_send_resources_get_mut() {
1503 let mut resources = NonSendResources::new();
1504 resources.insert(OpenGLContext {
1505 ctx: Rc::new(RefCell::new(1)),
1506 });
1507
1508 let ctx = resources.get_mut::<OpenGLContext>().unwrap();
1509 *ctx.ctx.borrow_mut() = 42;
1510
1511 assert_eq!(*resources.get::<OpenGLContext>().unwrap().ctx.borrow(), 42);
1512 }
1513
1514 #[test]
1515 fn test_non_send_resources_contains() {
1516 let mut resources = NonSendResources::new();
1517 assert!(!resources.contains::<WindowHandle>());
1518
1519 resources.insert(WindowHandle { id: Rc::new(42) });
1520 assert!(resources.contains::<WindowHandle>());
1521
1522 resources.remove::<WindowHandle>();
1523 assert!(!resources.contains::<WindowHandle>());
1524 }
1525
1526 #[test]
1527 fn test_non_send_resources_multiple_types() {
1528 let mut resources = NonSendResources::new();
1529 resources.insert(WindowHandle { id: Rc::new(1) });
1530 resources.insert(OpenGLContext {
1531 ctx: Rc::new(RefCell::new(2)),
1532 });
1533
1534 assert_eq!(resources.len(), 2);
1535 assert_eq!(*resources.get::<WindowHandle>().unwrap().id, 1);
1536 assert_eq!(*resources.get::<OpenGLContext>().unwrap().ctx.borrow(), 2);
1537 }
1538
1539 #[test]
1540 fn test_non_send_resources_clear() {
1541 let mut resources = NonSendResources::new();
1542 resources.insert(WindowHandle { id: Rc::new(1) });
1543 resources.insert(OpenGLContext {
1544 ctx: Rc::new(RefCell::new(2)),
1545 });
1546
1547 resources.clear();
1548 assert!(resources.is_empty());
1549 assert_eq!(resources.len(), 0);
1550 }
1551
1552 #[test]
1553 fn test_non_send_resources_debug() {
1554 let mut resources = NonSendResources::new();
1555 resources.insert(WindowHandle { id: Rc::new(42) });
1556
1557 let debug_str = format!("{:?}", resources);
1558 assert!(debug_str.contains("NonSendResources"));
1559 assert!(debug_str.contains("count"));
1560 }
1561
1562 #[test]
1563 fn test_non_send_resources_with_raw_pointer() {
1564 let mut value = 42u32;
1565 let mut resources = NonSendResources::new();
1566 resources.insert(RawPointerResource {
1567 ptr: &mut value as *mut u32,
1568 });
1569
1570 let res = resources.get::<RawPointerResource>().unwrap();
1571 assert!(!res.ptr.is_null());
1572 }
1573
1574 // =====================================================================
1575 // NonSendResources Thread Safety Tests
1576 // =====================================================================
1577
1578 #[test]
1579 fn test_non_send_resources_is_not_send() {
1580 // NonSendResources should NOT implement Send
1581 fn check_not_send<T>() {
1582 // This is a compile-time check - the test passes by compiling
1583 // We can't easily test !Send at runtime
1584 }
1585 check_not_send::<NonSendResources>();
1586 // The actual !Send is enforced by the NonSendMarker containing *const ()
1587 }
1588
1589 #[test]
1590 fn test_non_send_resources_is_not_sync() {
1591 // NonSendResources should NOT implement Sync
1592 fn check_not_sync<T>() {
1593 // This is a compile-time check - the test passes by compiling
1594 // We can't easily test !Sync at runtime
1595 }
1596 check_not_sync::<NonSendResources>();
1597 // The actual !Sync is enforced by the NonSendMarker containing *const ()
1598 }
1599
1600 // =====================================================================
1601 // NonSend<T> and NonSendMut<T> Wrapper Tests
1602 // =====================================================================
1603
1604 #[test]
1605 fn test_non_send_new() {
1606 let handle = WindowHandle { id: Rc::new(42) };
1607 let non_send = NonSend::new(&handle);
1608 assert_eq!(*non_send.id, 42);
1609 }
1610
1611 #[test]
1612 fn test_non_send_deref() {
1613 let handle = WindowHandle { id: Rc::new(42) };
1614 let non_send = NonSend::new(&handle);
1615 assert_eq!(*non_send.id, 42);
1616 }
1617
1618 #[test]
1619 fn test_non_send_into_inner() {
1620 let handle = WindowHandle { id: Rc::new(42) };
1621 let non_send = NonSend::new(&handle);
1622 let inner = non_send.into_inner();
1623 assert_eq!(*inner.id, 42);
1624 }
1625
1626 #[test]
1627 fn test_non_send_clone() {
1628 let handle = WindowHandle { id: Rc::new(42) };
1629 let non_send = NonSend::new(&handle);
1630 let cloned = non_send;
1631 // Both should be valid
1632 assert_eq!(*non_send.id, 42);
1633 assert_eq!(*cloned.id, 42);
1634 }
1635
1636 #[test]
1637 fn test_non_send_copy() {
1638 let handle = WindowHandle { id: Rc::new(42) };
1639 let non_send = NonSend::new(&handle);
1640 let copied = non_send;
1641 // Both should still be valid
1642 assert_eq!(*non_send.id, 42);
1643 assert_eq!(*copied.id, 42);
1644 }
1645
1646 #[test]
1647 fn test_non_send_mut_new() {
1648 let mut ctx = OpenGLContext {
1649 ctx: Rc::new(RefCell::new(1)),
1650 };
1651 let non_send_mut = NonSendMut::new(&mut ctx);
1652 assert_eq!(*non_send_mut.ctx.borrow(), 1);
1653 }
1654
1655 #[test]
1656 fn test_non_send_mut_deref() {
1657 let mut ctx = OpenGLContext {
1658 ctx: Rc::new(RefCell::new(1)),
1659 };
1660 let non_send_mut = NonSendMut::new(&mut ctx);
1661 assert_eq!(*non_send_mut.ctx.borrow(), 1);
1662 }
1663
1664 #[test]
1665 fn test_non_send_mut_deref_mut() {
1666 let mut ctx = OpenGLContext {
1667 ctx: Rc::new(RefCell::new(1)),
1668 };
1669 {
1670 let mut non_send_mut = NonSendMut::new(&mut ctx);
1671 *non_send_mut.ctx.borrow_mut() = 42;
1672 }
1673 assert_eq!(*ctx.ctx.borrow(), 42);
1674 }
1675
1676 #[test]
1677 fn test_non_send_mut_into_inner() {
1678 let mut ctx = OpenGLContext {
1679 ctx: Rc::new(RefCell::new(1)),
1680 };
1681 let non_send_mut = NonSendMut::new(&mut ctx);
1682 let inner = non_send_mut.into_inner();
1683 *inner.ctx.borrow_mut() = 42;
1684 assert_eq!(*ctx.ctx.borrow(), 42);
1685 }
1686
1687 // =====================================================================
1688 // Integration Tests
1689 // =====================================================================
1690
1691 #[test]
1692 fn test_non_send_resource_lifecycle() {
1693 let mut resources = NonSendResources::new();
1694
1695 // Insert
1696 resources.insert(WindowHandle { id: Rc::new(0) });
1697 assert!(resources.contains::<WindowHandle>());
1698
1699 // Modify (via Rc)
1700 let id = resources.get::<WindowHandle>().unwrap().id.clone();
1701 assert_eq!(*id, 0);
1702
1703 // Replace
1704 resources.insert(WindowHandle { id: Rc::new(42) });
1705 assert_eq!(*resources.get::<WindowHandle>().unwrap().id, 42);
1706
1707 // Remove
1708 let removed = resources.remove::<WindowHandle>();
1709 assert_eq!(*removed.unwrap().id, 42);
1710 assert!(!resources.contains::<WindowHandle>());
1711 }
1712 }
1713}