1use std::marker::PhantomData;
4
5use crate::system_param::{Access, SystemParam};
6use crate::world::{UnsafeWorldCell, World};
7
8pub trait System {
14 fn name(&self) -> &'static str;
16
17 fn run(&mut self, world: &mut World);
19
20 fn access(&self) -> Vec<Access>;
24}
25
26pub trait IntoSystem<Params> {
35 fn into_system(self, name: &'static str) -> Box<dyn System>;
36}
37
38pub(crate) trait SystemParamFunction<Params>: 'static {
44 fn run(&mut self, world: &mut World);
45 fn param_access() -> Vec<Access>;
46}
47
48struct ParamSystem<F, Params> {
49 name: &'static str,
50 func: F,
51 _marker: PhantomData<fn() -> Params>,
52}
53
54impl<F, Params> System for ParamSystem<F, Params>
55where
56 F: SystemParamFunction<Params>,
57{
58 fn name(&self) -> &'static str {
59 self.name
60 }
61
62 fn run(&mut self, world: &mut World) {
63 self.func.run(world);
64 }
65
66 fn access(&self) -> Vec<Access> {
67 F::param_access()
68 }
69}
70
71fn validate_no_self_conflicts(access: &[Access], system_name: &'static str) {
73 for (i, a) in access.iter().enumerate() {
74 for b in &access[i + 1..] {
75 if a.conflicts_with(b) {
76 panic!(
77 "system '{}' has conflicting parameter access: {:?} vs {:?}",
78 system_name, a, b,
79 );
80 }
81 }
82 }
83}
84
85impl<Func> SystemParamFunction<()> for Func
86where
87 Func: FnMut() + 'static,
88{
89 fn run(&mut self, _world: &mut World) {
90 self();
91 }
92
93 fn param_access() -> Vec<Access> {
94 Vec::new()
95 }
96}
97
98impl<Func> IntoSystem<()> for Func
99where
100 Func: SystemParamFunction<()>,
101{
102 fn into_system(self, name: &'static str) -> Box<dyn System> {
103 Box::new(ParamSystem {
104 name,
105 func: self,
106 _marker: PhantomData,
107 })
108 }
109}
110
111macro_rules! impl_system_param_function {
116 ($($P:ident),+) => {
117 #[allow(non_snake_case)]
118 impl<Func, $($P: SystemParam + 'static),+> SystemParamFunction<($($P,)+)> for Func
119 where
120 Func: FnMut($($P::Item<'_>),+) + 'static,
121 {
122 fn run(&mut self, world: &mut World) {
123 let cell = unsafe { UnsafeWorldCell::new(world as *mut World) };
130 unsafe {
131 self($($P::fetch(cell),)+);
132 }
133 }
134
135 fn param_access() -> Vec<Access> {
136 let mut acc = Vec::new();
137 $(acc.extend($P::access());)+
138 acc
139 }
140 }
141
142 impl<Func, $($P: SystemParam + 'static),+> IntoSystem<($($P,)+)> for Func
143 where
144 Func: SystemParamFunction<($($P,)+)>,
145 {
146 fn into_system(self, name: &'static str) -> Box<dyn System> {
147 let access = <Func as SystemParamFunction<($($P,)+)>>::param_access();
148 validate_no_self_conflicts(&access, name);
149 Box::new(ParamSystem {
150 name,
151 func: self,
152 _marker: PhantomData,
153 })
154 }
155 }
156 };
157}
158
159impl_system_param_function!(P0);
160impl_system_param_function!(P0, P1);
161impl_system_param_function!(P0, P1, P2);
162impl_system_param_function!(P0, P1, P2, P3);
163impl_system_param_function!(P0, P1, P2, P3, P4);
164impl_system_param_function!(P0, P1, P2, P3, P4, P5);
165impl_system_param_function!(P0, P1, P2, P3, P4, P5, P6);
166impl_system_param_function!(P0, P1, P2, P3, P4, P5, P6, P7);
167
168#[cfg(test)]
173mod tests {
174 use super::*;
175 use crate::component::Component;
176 use crate::system_param::{Query, QueryMut, Res, ResMut};
177
178 #[derive(Debug)]
179 struct Counter(u32);
180 impl Component for Counter {}
181
182 struct Speed(f32);
183
184 fn count_entities(query: Query<'_, Counter>) {
185 let _ = query.len();
186 }
187
188 #[test]
189 fn one_param_into_system() {
190 let mut sys: Box<dyn System> =
191 IntoSystem::<(Query<'_, Counter>,)>::into_system(count_entities, "count");
192 let mut world = World::new();
193 world.spawn((Counter(0),));
194 sys.run(&mut world);
195 }
196
197 fn noop() {}
198
199 #[test]
200 fn zero_param_into_system() {
201 let mut sys: Box<dyn System> = IntoSystem::<()>::into_system(noop, "noop");
202 let mut world = World::new();
203 sys.run(&mut world);
204 assert!(sys.access().is_empty());
205 }
206
207 fn read_speed_count_entities(speed: Res<'_, Speed>, query: Query<'_, Counter>) {
208 let _ = *speed;
209 let _ = query.len();
210 }
211
212 #[test]
213 fn two_param_into_system() {
214 let mut sys: Box<dyn System> =
215 IntoSystem::<(Res<'_, Speed>, Query<'_, Counter>)>::into_system(
216 read_speed_count_entities,
217 "two_param",
218 );
219 let mut world = World::new();
220 world.insert_resource(Speed(1.5));
221 world.spawn((Counter(0),));
222 sys.run(&mut world);
223 }
224
225 fn increment_speed(mut speed: ResMut<'_, Speed>) {
226 speed.0 += 1.0;
227 }
228
229 #[test]
230 fn res_mut_system_mutates() {
231 let mut sys: Box<dyn System> =
232 IntoSystem::<(ResMut<'_, Speed>,)>::into_system(increment_speed, "inc_speed");
233 let mut world = World::new();
234 world.insert_resource(Speed(0.0));
235 sys.run(&mut world);
236 assert!((world.resource::<Speed>().0 - 1.0).abs() < f32::EPSILON);
237 }
238
239 fn increment_counters(mut counters: QueryMut<'_, Counter>) {
240 for (_, c) in counters.iter_mut() {
241 c.0 += 1;
242 }
243 }
244
245 #[test]
246 fn query_mut_system_mutates() {
247 let mut sys: Box<dyn System> =
248 IntoSystem::<(QueryMut<'_, Counter>,)>::into_system(increment_counters, "inc_counters");
249 let mut world = World::new();
250 world.spawn((Counter(0),));
251 world.spawn((Counter(10),));
252 sys.run(&mut world);
253 let mut vals: Vec<u32> = world.query::<&Counter>().map(|(_, c)| c.0).collect();
254 vals.sort();
255 assert_eq!(vals, vec![1, 11]);
256 }
257
258 fn conflicting_system(_a: Res<'_, Speed>, _b: ResMut<'_, Speed>) {}
259
260 #[test]
261 #[should_panic(expected = "conflicting parameter access")]
262 fn self_conflict_panics_on_registration() {
263 let _ = IntoSystem::<(Res<'_, Speed>, ResMut<'_, Speed>)>::into_system(
264 conflicting_system,
265 "conflict",
266 );
267 }
268
269 #[test]
270 fn system_reports_access() {
271 let sys: Box<dyn System> =
272 IntoSystem::<(ResMut<'_, Speed>,)>::into_system(increment_speed, "inc_speed");
273 let access = sys.access();
274 assert_eq!(access.len(), 1);
275 }
276}