Skip to main content

goud_engine/ecs/system/system_param/
resource_params.rs

1//! [`SystemParam`] implementations for resource access types.
2//!
3//! Covers [`Res`], [`ResMut`], [`NonSend`], and [`NonSendMut`].
4
5use crate::ecs::query::Access;
6use crate::ecs::resource::{NonSend, NonSendMut, NonSendResource, NonSendResourceId};
7use crate::ecs::resource::{Res, ResMut, Resource, ResourceId};
8use crate::ecs::World;
9
10use super::traits::{ReadOnlySystemParam, SystemParam, SystemParamState};
11
12// =============================================================================
13// Res<T> - Immutable Resource SystemParam
14// =============================================================================
15
16/// State for `Res<T>` system parameter.
17///
18/// Caches the resource ID for efficient access and conflict detection.
19#[derive(Debug, Clone)]
20pub struct ResState<T: Resource> {
21    _marker: std::marker::PhantomData<T>,
22}
23
24impl<T: Resource> SystemParamState for ResState<T> {
25    fn init(_world: &mut World) -> Self {
26        Self {
27            _marker: std::marker::PhantomData,
28        }
29    }
30}
31
32/// `Res<T>` as a system parameter for immutable resource access.
33///
34/// # Example
35///
36/// ```ignore
37/// fn print_time(time: Res<Time>) {
38///     println!("Delta: {}", time.delta);
39/// }
40/// ```
41///
42/// # Panics
43///
44/// When used as a system parameter, `Res<T>` will panic if the resource
45/// does not exist in the world. Use `Option<Res<T>>` for optional access.
46impl<T: Resource> SystemParam for Res<'_, T> {
47    type State = ResState<T>;
48    type Item<'w, 's> = Res<'w, T>;
49
50    fn update_access(_state: &Self::State, access: &mut Access) {
51        // Register read access to the resource
52        access.add_resource_read(ResourceId::of::<T>());
53    }
54
55    fn get_param<'w, 's>(_state: &'s mut Self::State, world: &'w World) -> Self::Item<'w, 's> {
56        world
57            .resource::<T>()
58            .expect("Resource does not exist. Use Option<Res<T>> for optional access.")
59    }
60
61    fn get_param_mut<'w, 's>(
62        state: &'s mut Self::State,
63        world: &'w mut World,
64    ) -> Self::Item<'w, 's> {
65        // Immutable resource access, so we just use get_param
66        Self::get_param(state, world)
67    }
68}
69
70/// `Res<T>` is read-only.
71impl<T: Resource> ReadOnlySystemParam for Res<'_, T> {}
72
73// =============================================================================
74// ResMut<T> - Mutable Resource SystemParam
75// =============================================================================
76
77/// State for `ResMut<T>` system parameter.
78///
79/// Caches the resource ID for efficient access and conflict detection.
80#[derive(Debug, Clone)]
81pub struct ResMutState<T: Resource> {
82    _marker: std::marker::PhantomData<T>,
83}
84
85impl<T: Resource> SystemParamState for ResMutState<T> {
86    fn init(_world: &mut World) -> Self {
87        Self {
88            _marker: std::marker::PhantomData,
89        }
90    }
91}
92
93/// `ResMut<T>` as a system parameter for mutable resource access.
94///
95/// # Example
96///
97/// ```ignore
98/// fn update_time(mut time: ResMut<Time>) {
99///     time.total += time.delta;
100/// }
101/// ```
102///
103/// # Panics
104///
105/// When used as a system parameter, `ResMut<T>` will panic if the resource
106/// does not exist in the world. Use `Option<ResMut<T>>` for optional access.
107impl<T: Resource> SystemParam for ResMut<'_, T> {
108    type State = ResMutState<T>;
109    type Item<'w, 's> = ResMut<'w, T>;
110
111    fn update_access(_state: &Self::State, access: &mut Access) {
112        // Register write access to the resource
113        access.add_resource_write(ResourceId::of::<T>());
114    }
115
116    fn get_param<'w, 's>(_state: &'s mut Self::State, _world: &'w World) -> Self::Item<'w, 's> {
117        // ResMut requires mutable world access, so this panics
118        panic!("ResMut<T> requires mutable world access. Use get_param_mut instead.")
119    }
120
121    fn get_param_mut<'w, 's>(
122        _state: &'s mut Self::State,
123        world: &'w mut World,
124    ) -> Self::Item<'w, 's> {
125        world
126            .resource_mut::<T>()
127            .expect("Resource does not exist. Use Option<ResMut<T>> for optional access.")
128    }
129}
130
131// ResMut is NOT ReadOnlySystemParam - intentionally omitted
132
133// =============================================================================
134// NonSend<T> - Immutable Non-Send Resource SystemParam
135// =============================================================================
136
137/// State for `NonSend<T>` system parameter.
138///
139/// Caches the non-send resource ID for efficient access and conflict detection.
140///
141/// Note: Uses `PhantomData<fn() -> T>` to be Send+Sync regardless of T's bounds.
142/// This is safe because the state only stores type information for conflict detection,
143/// not the actual resource data.
144#[derive(Debug, Clone)]
145pub struct NonSendState<T: NonSendResource> {
146    // Use fn() -> T to be Send + Sync regardless of T's bounds
147    _marker: std::marker::PhantomData<fn() -> T>,
148}
149
150impl<T: NonSendResource> SystemParamState for NonSendState<T> {
151    fn init(_world: &mut World) -> Self {
152        Self {
153            _marker: std::marker::PhantomData,
154        }
155    }
156}
157
158/// `NonSend<T>` as a system parameter for immutable non-send resource access.
159///
160/// # Thread Safety
161///
162/// Systems using `NonSend<T>` are constrained to run on the main thread.
163/// This is enforced by the scheduler.
164///
165/// # Example
166///
167/// ```ignore
168/// fn print_window(window: NonSend<WindowHandle>) {
169///     println!("Window ID: {}", window.id);
170/// }
171/// ```
172///
173/// # Panics
174///
175/// When used as a system parameter, `NonSend<T>` will panic if the non-send
176/// resource does not exist in the world.
177impl<T: NonSendResource> SystemParam for NonSend<'_, T> {
178    type State = NonSendState<T>;
179    type Item<'w, 's> = NonSend<'w, T>;
180
181    fn update_access(_state: &Self::State, access: &mut Access) {
182        // Register read access to the non-send resource
183        access.add_non_send_read(NonSendResourceId::of::<T>());
184    }
185
186    fn get_param<'w, 's>(_state: &'s mut Self::State, world: &'w World) -> Self::Item<'w, 's> {
187        world
188            .non_send_resource::<T>()
189            .expect("Non-send resource does not exist. Use Option<NonSend<T>> for optional access.")
190    }
191
192    fn get_param_mut<'w, 's>(
193        state: &'s mut Self::State,
194        world: &'w mut World,
195    ) -> Self::Item<'w, 's> {
196        // Immutable non-send resource access, so we just use get_param
197        Self::get_param(state, world)
198    }
199}
200
201/// `NonSend<T>` is read-only.
202impl<T: NonSendResource> ReadOnlySystemParam for NonSend<'_, T> {}
203
204// =============================================================================
205// NonSendMut<T> - Mutable Non-Send Resource SystemParam
206// =============================================================================
207
208/// State for `NonSendMut<T>` system parameter.
209///
210/// Caches the non-send resource ID for efficient access and conflict detection.
211///
212/// Note: Uses `PhantomData<fn() -> T>` to be Send+Sync regardless of T's bounds.
213/// This is safe because the state only stores type information for conflict detection,
214/// not the actual resource data.
215#[derive(Debug, Clone)]
216pub struct NonSendMutState<T: NonSendResource> {
217    // Use fn() -> T to be Send + Sync regardless of T's bounds
218    _marker: std::marker::PhantomData<fn() -> T>,
219}
220
221impl<T: NonSendResource> SystemParamState for NonSendMutState<T> {
222    fn init(_world: &mut World) -> Self {
223        Self {
224            _marker: std::marker::PhantomData,
225        }
226    }
227}
228
229/// `NonSendMut<T>` as a system parameter for mutable non-send resource access.
230///
231/// # Thread Safety
232///
233/// Systems using `NonSendMut<T>` are constrained to run on the main thread.
234/// This is enforced by the scheduler.
235///
236/// # Example
237///
238/// ```ignore
239/// fn update_window(mut window: NonSendMut<WindowHandle>) {
240///     window.update_title("New Title");
241/// }
242/// ```
243///
244/// # Panics
245///
246/// When used as a system parameter, `NonSendMut<T>` will panic if the non-send
247/// resource does not exist in the world.
248impl<T: NonSendResource> SystemParam for NonSendMut<'_, T> {
249    type State = NonSendMutState<T>;
250    type Item<'w, 's> = NonSendMut<'w, T>;
251
252    fn update_access(_state: &Self::State, access: &mut Access) {
253        // Register write access to the non-send resource
254        access.add_non_send_write(NonSendResourceId::of::<T>());
255    }
256
257    fn get_param<'w, 's>(_state: &'s mut Self::State, _world: &'w World) -> Self::Item<'w, 's> {
258        // NonSendMut requires mutable world access, so this panics
259        panic!("NonSendMut<T> requires mutable world access. Use get_param_mut instead.")
260    }
261
262    fn get_param_mut<'w, 's>(
263        _state: &'s mut Self::State,
264        world: &'w mut World,
265    ) -> Self::Item<'w, 's> {
266        world.non_send_resource_mut::<T>().expect(
267            "Non-send resource does not exist. Use Option<NonSendMut<T>> for optional access.",
268        )
269    }
270}
271
272// NonSendMut is NOT ReadOnlySystemParam - intentionally omitted