Skip to main content

gizmo_core/system/
params.rs

1use super::*;
2use crate::world::World;
3use std::any::TypeId;
4
5// ==============================================================
6// DEPENDENCY INJECTION SİSTEMİ
7// ==============================================================
8
9use crate::world::{ResourceReadGuard, ResourceWriteGuard};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum SystemParamFetchError {
14    Resource(crate::world::ResourceFetchError),
15    QueryError,
16}
17
18impl From<crate::world::ResourceFetchError> for SystemParamFetchError {
19    fn from(value: crate::world::ResourceFetchError) -> Self {
20        Self::Resource(value)
21    }
22}
23
24impl std::fmt::Display for SystemParamFetchError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            SystemParamFetchError::Resource(e) => {
28                write!(f, "system parameter resource fetch failed: {e}")
29            }
30            SystemParamFetchError::QueryError => {
31                write!(f, "system parameter query construction failed")
32            }
33        }
34    }
35}
36
37impl std::error::Error for SystemParamFetchError {
38    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
39        match self {
40            SystemParamFetchError::Resource(e) => Some(e),
41            SystemParamFetchError::QueryError => None,
42        }
43    }
44}
45
46// SystemParam tamamen içsel bir DI trait'idir; yanlış bir impl scheduler'ın
47// aliasing garantilerini bozar. Tüm impl'ler bu crate içindedir (Res/ResMut/
48// f32/Query) ve cross-crate impl yoktur, bu yüzden sealed yapılır.
49// `pub(crate)` çünkü SystemParam'ı implemente eden tipler bu crate'in başka
50// modüllerinde de var (EventReader/EventWriter @ event.rs, Commands @ commands.rs);
51// Sealed'a yalnızca crate içinden erişilebilir, dolayısıyla dış crate'ler hâlâ
52// SystemParam impl edemez.
53pub(crate) mod sealed {
54    pub trait Sealed {}
55}
56
57/// A value that a system can request as a parameter (e.g. [`Query`](crate::Query),
58/// [`Res`], [`ResMut`]).
59///
60/// Implementors describe how to fetch their value from the [`World`] and which
61/// component/resource accesses they require, allowing the scheduler to run
62/// non-conflicting systems in parallel.
63pub trait SystemParam: sealed::Sealed {
64    type Item<'w>;
65    fn fetch<'w>(world: &'w World, dt: f32) -> Result<Self::Item<'w>, SystemParamFetchError>;
66    fn get_access_info(info: &mut AccessInfo);
67}
68
69pub struct Res<'w, T: 'static> {
70    value: ResourceReadGuard<'w, T>,
71}
72
73impl<'w, T: 'static> std::ops::Deref for Res<'w, T> {
74    type Target = T;
75    fn deref(&self) -> &Self::Target {
76        &self.value
77    }
78}
79
80impl<T: 'static> sealed::Sealed for Res<'static, T> {}
81impl<T: 'static> SystemParam for Res<'static, T> {
82    type Item<'w> = Res<'w, T>;
83    fn fetch<'w>(world: &'w World, _dt: f32) -> Result<Self::Item<'w>, SystemParamFetchError> {
84        let value = world.try_get_resource::<T>()?;
85        Ok(Res::<T> { value })
86    }
87    fn get_access_info(info: &mut AccessInfo) {
88        info.resource_reads.push(TypeId::of::<T>());
89    }
90}
91
92pub struct ResMut<'w, T: 'static> {
93    value: ResourceWriteGuard<'w, T>,
94}
95
96impl<'w, T: 'static> std::ops::Deref for ResMut<'w, T> {
97    type Target = T;
98    fn deref(&self) -> &Self::Target {
99        &self.value
100    }
101}
102
103impl<'w, T: 'static> std::ops::DerefMut for ResMut<'w, T> {
104    fn deref_mut(&mut self) -> &mut Self::Target {
105        &mut self.value
106    }
107}
108
109impl<T: 'static> sealed::Sealed for ResMut<'static, T> {}
110impl<T: 'static> SystemParam for ResMut<'static, T> {
111    type Item<'w> = ResMut<'w, T>;
112    fn fetch<'w>(world: &'w World, _dt: f32) -> Result<Self::Item<'w>, SystemParamFetchError> {
113        let value = world.try_get_resource_mut::<T>()?;
114        Ok(ResMut::<T> { value })
115    }
116    fn get_access_info(info: &mut AccessInfo) {
117        info.resource_writes.push(TypeId::of::<T>());
118    }
119}
120
121impl sealed::Sealed for f32 {}
122impl SystemParam for f32 {
123    type Item<'w> = f32;
124    fn fetch<'w>(_world: &'w World, dt: f32) -> Result<Self::Item<'w>, SystemParamFetchError> {
125        Ok(dt)
126    }
127    fn get_access_info(_info: &mut AccessInfo) {}
128}
129
130impl<Q: crate::query::WorldQuery + 'static> sealed::Sealed for crate::query::Query<'static, Q> {}
131impl<Q: crate::query::WorldQuery + 'static> SystemParam for crate::query::Query<'static, Q> {
132    type Item<'w> = crate::query::Query<'w, Q>;
133    fn fetch<'w>(world: &'w World, _dt: f32) -> Result<Self::Item<'w>, SystemParamFetchError> {
134        // SAFETY: the scheduler validates that co-batched systems have disjoint component
135        // access (`AccessInfo`/`is_compatible_with`) before running them in parallel, and
136        // runs `is_exclusive` systems alone. So while this system's `Query` is live, no
137        // other query mutably aliases the same components. This is the documented contract
138        // of `query_unchecked` — the safe `query`/`query_mut` split can't express it because
139        // a system only ever holds a shared `&World`.
140        if let Some(query) = unsafe { world.query_unchecked::<Q>() } {
141            Ok(query)
142        } else {
143            Err(SystemParamFetchError::QueryError)
144        }
145    }
146    fn get_access_info(info: &mut AccessInfo) {
147        let mut types = Vec::new();
148        Q::check_aliasing(&mut types);
149        for (tid, is_mut) in types {
150            if is_mut {
151                info.component_writes.push(tid);
152            } else {
153                info.component_reads.push(tid);
154            }
155        }
156    }
157}
158