shred/system.rs
1use std::{marker::PhantomData, ops::Deref};
2
3use crate::{ResourceId, World};
4
5/// A trait for accessing read/write multiple resources from a system. This can
6/// be used to create dynamic systems that don't specify what they fetch at
7/// compile-time.
8///
9/// For compile-time system data this will all be done for you using
10/// `StaticAccessor`.
11pub trait Accessor: Sized {
12 /// Tries to create a new instance of this type. This one returns `Some` in
13 /// case there is a default, otherwise the system needs to override
14 /// `System::accessor`.
15 fn try_new() -> Option<Self>;
16
17 /// A list of [`ResourceId`]s the bundle
18 /// needs read access to in order to
19 /// build the target resource bundle.
20 ///
21 /// # Contract
22 ///
23 /// Exactly return the dependencies you're going to `fetch`! Doing otherwise
24 /// *will* cause a panic.
25 ///
26 /// This method is only executed once,
27 /// thus the returned value may never change
28 /// (otherwise it has no effect).
29 ///
30 /// [`ResourceId`]: struct.ResourceId.html
31 fn reads(&self) -> Vec<ResourceId>;
32
33 /// A list of [`ResourceId`]s the bundle
34 /// needs write access to in order to
35 /// build the target resource bundle.
36 ///
37 /// # Contract
38 ///
39 /// Exactly return the dependencies you're going to `fetch`! Doing otherwise
40 /// *will* cause a panic.
41 ///
42 /// This method is only executed once,
43 /// thus the returned value may never change
44 /// (otherwise it has no effect).
45 ///
46 /// [`ResourceId`]: struct.ResourceId.html
47 fn writes(&self) -> Vec<ResourceId>;
48}
49
50impl Accessor for () {
51 fn try_new() -> Option<Self> {
52 None
53 }
54
55 fn reads(&self) -> Vec<ResourceId> {
56 Vec::new()
57 }
58
59 fn writes(&self) -> Vec<ResourceId> {
60 Vec::new()
61 }
62}
63
64impl<T: ?Sized> Accessor for PhantomData<T> {
65 fn try_new() -> Option<Self> {
66 None
67 }
68
69 fn reads(&self) -> Vec<ResourceId> {
70 Vec::new()
71 }
72
73 fn writes(&self) -> Vec<ResourceId> {
74 Vec::new()
75 }
76}
77
78/// Either an `Accessor` of the system `T` or a reference to it.
79pub enum AccessorCow<'a, 'b, T>
80where
81 AccessorTy<'a, T>: 'b,
82 T: System<'a> + ?Sized,
83 'a: 'b,
84{
85 /// A reference to an accessor.
86 Ref(&'b AccessorTy<'a, T>),
87 /// An owned accessor.
88 Owned(AccessorTy<'a, T>),
89}
90
91impl<'a, 'b, T> Deref for AccessorCow<'a, 'b, T>
92where
93 AccessorTy<'a, T>: 'b,
94 T: System<'a> + ?Sized + 'b,
95 'a: 'b,
96{
97 type Target = AccessorTy<'a, T>;
98
99 fn deref(&self) -> &AccessorTy<'a, T> {
100 match self {
101 AccessorCow::Ref(r) => r,
102 AccessorCow::Owned(ref o) => o,
103 }
104 }
105}
106
107type AccessorTy<'a, T> = <<T as System<'a>>::SystemData as DynamicSystemData<'a>>::Accessor;
108
109/// Trait for fetching data and running systems. Automatically implemented for
110/// systems.
111pub trait RunNow<'a> {
112 /// Runs the system now.
113 ///
114 /// # Panics
115 ///
116 /// Panics if the system tries to fetch resources
117 /// which are borrowed in an incompatible way already
118 /// (tries to read from a resource which is already written to or
119 /// tries to write to a resource which is read from).
120 fn run_now(&mut self, world: &'a World);
121
122 /// Sets up `World` for a later call to `run_now`.
123 fn setup(&mut self, world: &mut World);
124
125 /// Performs clean up that requires resources from the `World`.
126 /// This commonly removes components from `world` which depend on external
127 /// resources.
128 #[allow(clippy::boxed_local)]
129 fn dispose(self: Box<Self>, world: &mut World) {
130 let _ = world;
131 }
132}
133
134impl<'a, T> RunNow<'a> for T
135where
136 T: System<'a>,
137{
138 fn run_now(&mut self, world: &'a World) {
139 let data = T::SystemData::fetch(&self.accessor(), world);
140 self.run(data);
141 }
142
143 fn setup(&mut self, world: &mut World) {
144 T::setup(self, world);
145 }
146
147 fn dispose(self: Box<Self>, world: &mut World) {
148 T::dispose(*self, world);
149 }
150}
151
152#[repr(u8)]
153#[allow(missing_docs)]
154#[derive(Clone, Copy, Debug)]
155pub enum RunningTime {
156 VeryShort = 1,
157 Short = 2,
158 Average = 3,
159 Long = 4,
160 VeryLong = 5,
161}
162
163/// A `System`, executed with a set of required [`Resource`]s.
164///
165/// [`Resource`]: trait.Resource.html
166pub trait System<'a> {
167 /// The resource bundle required to execute this system.
168 ///
169 /// You will mostly use a tuple of system data (which also implements
170 /// `SystemData`). You can also create such a resource bundle by simply
171 /// deriving `SystemData` for a struct.
172 ///
173 /// Every `SystemData` is also a `DynamicSystemData`.
174 type SystemData: DynamicSystemData<'a>;
175
176 /// Executes the system with the required system
177 /// data.
178 fn run(&mut self, data: Self::SystemData);
179
180 /// Returns a hint how long the system needs for running.
181 /// This is used to optimize the way they're executed (might
182 /// allow more parallelization).
183 ///
184 /// Defaults to `RunningTime::Average`.
185 fn running_time(&self) -> RunningTime {
186 RunningTime::Average
187 }
188
189 /// Return the accessor from the [`SystemData`].
190 fn accessor<'b>(&'b self) -> AccessorCow<'a, 'b, Self> {
191 AccessorCow::Owned(
192 AccessorTy::<'a, Self>::try_new().expect("Missing implementation for `accessor`"),
193 )
194 }
195
196 /// Sets up the `World` using `Self::SystemData::setup`.
197 fn setup(&mut self, world: &mut World) {
198 <Self::SystemData as DynamicSystemData>::setup(&self.accessor(), world)
199 }
200
201 /// Performs clean up that requires resources from the `World`.
202 /// This commonly removes components from `world` which depend on external
203 /// resources.
204 fn dispose(self, world: &mut World)
205 where
206 Self: Sized,
207 {
208 let _ = world;
209 }
210}
211
212/// A static system data that can specify its dependencies at statically (at
213/// compile-time). Most system data is a `SystemData`, the `DynamicSystemData`
214/// type is only needed for very special setups.
215///
216/// You can derive this using the `#[derive(SystemData)]` macro provided by
217/// `shred-derive`. That is as simple as enabling the `shred-derive` feature.
218///
219/// # Examples
220///
221/// ```rust
222/// use shred::{Read, ResourceId, SystemData, World, Write};
223///
224/// #[derive(Default)]
225/// pub struct Clock;
226/// #[derive(Default)]
227/// pub struct Timer;
228///
229/// // This will implement `SystemData` for `MySystemData`.
230/// // Please note that this will only work if `SystemData`, `World` and `ResourceId` are included.
231/// # #[cfg(feature = "shred-derive")]
232/// #[derive(SystemData)]
233/// pub struct MySystemData<'a> {
234/// pub clock: Read<'a, Clock>,
235/// pub timer: Write<'a, Timer>,
236/// }
237/// #
238/// # // The following is required for the snippet to compile without the `shred-derive` feature.
239/// #
240/// # #[cfg(not(feature = "shred-derive"))]
241/// # struct MySystemData<'a> {
242/// # pub clock: Read<'a, Clock>,
243/// # pub timer: Write<'a, Timer>,
244/// # }
245/// #
246/// # #[cfg(not(feature = "shred-derive"))]
247/// # impl<'a> SystemData<'a> for MySystemData<'a> {
248/// # fn setup(world: &mut World) {
249/// # Read::<'_, Clock>::setup(world);
250/// # Write::<'_, Timer>::setup(world);
251/// # }
252/// #
253/// # fn fetch(world: &'a World) -> Self {
254/// # Self {
255/// # clock: Read::<'_, Clock>::fetch(world),
256/// # timer: Write::<'_, Timer>::fetch(world),
257/// # }
258/// # }
259/// #
260/// # fn reads() -> Vec<ResourceId> {
261/// # Read::<'_, Clock>::reads()
262/// # }
263/// #
264/// # fn writes() -> Vec<ResourceId> {
265/// # Write::<'_, Timer>::writes()
266/// # }
267/// # }
268/// ```
269pub trait SystemData<'a> {
270 /// Sets up the system data for fetching it from the `World`.
271 fn setup(world: &mut World);
272
273 /// Fetches the system data from `World`. Note that this is only specified
274 /// for one concrete lifetime `'a`, you need to implement the
275 /// `SystemData` trait for every possible lifetime.
276 fn fetch(world: &'a World) -> Self;
277
278 /// Returns all read dependencies as fetched from `Self::fetch`.
279 ///
280 /// Please note that returning wrong dependencies can lead to a panic.
281 fn reads() -> Vec<ResourceId>;
282
283 /// Returns all write dependencies as fetched from `Self::fetch`.
284 ///
285 /// Please note that returning wrong dependencies can lead to a panic.
286 fn writes() -> Vec<ResourceId>;
287}
288
289impl<'a, T> DynamicSystemData<'a> for T
290where
291 T: SystemData<'a>,
292{
293 type Accessor = StaticAccessor<T>;
294
295 fn setup(_: &StaticAccessor<T>, world: &mut World) {
296 T::setup(world);
297 }
298
299 fn fetch(_: &StaticAccessor<T>, world: &'a World) -> Self {
300 T::fetch(world)
301 }
302}
303
304impl<'a> SystemData<'a> for () {
305 fn setup(_: &mut World) {}
306
307 fn fetch(_: &'a World) -> Self {}
308
309 fn reads() -> Vec<ResourceId> {
310 Vec::new()
311 }
312
313 fn writes() -> Vec<ResourceId> {
314 Vec::new()
315 }
316}
317
318/// The static accessor that is used for `SystemData`.
319#[derive(Default)]
320pub struct StaticAccessor<T> {
321 marker: PhantomData<fn() -> T>,
322}
323
324impl<'a, T> Accessor for StaticAccessor<T>
325where
326 T: SystemData<'a>,
327{
328 fn try_new() -> Option<Self> {
329 Some(StaticAccessor {
330 marker: PhantomData,
331 })
332 }
333
334 fn reads(&self) -> Vec<ResourceId> {
335 T::reads()
336 }
337
338 fn writes(&self) -> Vec<ResourceId> {
339 T::writes()
340 }
341}
342
343/// A struct implementing system data indicates that it bundles some resources
344/// which are required for the execution.
345///
346/// This is the more flexible, but complex variant of `SystemData`.
347pub trait DynamicSystemData<'a> {
348 /// The accessor of the `SystemData`, which specifies the read and write
349 /// dependencies and does the fetching.
350 type Accessor: Accessor;
351
352 /// Sets up `World` for fetching this system data.
353 fn setup(accessor: &Self::Accessor, world: &mut World);
354
355 /// Creates a new resource bundle
356 /// by fetching the required resources
357 /// from the [`World`] struct.
358 ///
359 /// # Contract
360 ///
361 /// Only fetch the resources you returned from `reads` / `writes`!
362 ///
363 /// # Panics
364 ///
365 /// This function may panic if the above contract is violated.
366 /// This function may panic if the resource doesn't exist. This is only the
367 /// case if either `setup` was not called or it didn't insert any
368 /// fallback value.
369 ///
370 /// [`World`]: trait.World.html
371 fn fetch(access: &Self::Accessor, world: &'a World) -> Self;
372}
373
374impl<'a, T: ?Sized> SystemData<'a> for PhantomData<T> {
375 fn setup(_: &mut World) {}
376
377 fn fetch(_: &World) -> Self {
378 PhantomData
379 }
380
381 fn reads() -> Vec<ResourceId> {
382 vec![]
383 }
384
385 fn writes() -> Vec<ResourceId> {
386 vec![]
387 }
388}
389
390macro_rules! impl_data {
391 ( $($ty:ident),* ) => {
392 impl<'a, $($ty),*> SystemData<'a> for ( $( $ty , )* )
393 where $( $ty : SystemData<'a> ),*
394 {
395 fn setup(world: &mut World) {
396 #![allow(unused_variables)]
397
398 $(
399 <$ty as SystemData>::setup(&mut *world);
400 )*
401 }
402
403 fn fetch(world: &'a World) -> Self {
404 #![allow(unused_variables)]
405
406 ( $( <$ty as SystemData<'a>>::fetch(world), )* )
407 }
408
409 fn reads() -> Vec<ResourceId> {
410 #![allow(unused_mut)]
411
412 let mut r = Vec::new();
413
414 $( {
415 let mut reads = <$ty as SystemData>::reads();
416 r.append(&mut reads);
417 } )*
418
419 r
420 }
421
422 fn writes() -> Vec<ResourceId> {
423 #![allow(unused_mut)]
424
425 let mut r = Vec::new();
426
427 $( {
428 let mut writes = <$ty as SystemData>::writes();
429 r.append(&mut writes);
430 } )*
431
432 r
433 }
434 }
435 };
436}
437
438mod impl_data {
439 #![cfg_attr(rustfmt, rustfmt_skip)]
440
441 use super::*;
442
443 impl_data!(A);
444 impl_data!(A, B);
445 impl_data!(A, B, C);
446 impl_data!(A, B, C, D);
447 impl_data!(A, B, C, D, E);
448 impl_data!(A, B, C, D, E, F);
449 impl_data!(A, B, C, D, E, F, G);
450 impl_data!(A, B, C, D, E, F, G, H);
451 impl_data!(A, B, C, D, E, F, G, H, I);
452 impl_data!(A, B, C, D, E, F, G, H, I, J);
453 impl_data!(A, B, C, D, E, F, G, H, I, J, K);
454 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L);
455 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M);
456 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
457 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
458 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
459 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
460 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
461 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
462 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
463 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
464 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
465 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
466 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
467 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
468 impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
469}