limnus_stage/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/limnus
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use limnus_system::{IntoSystem, System, SystemParam};
6use limnus_system_state::State;
7use std::any::TypeId;
8use std::collections::HashMap;
9use std::fmt::{Debug, Formatter};
10
11/// A marker trait used to uniquely identify stages.
12///
13/// Implement this trait for any type you wish to use as a stage identifier.
14pub trait StageTag: 'static {}
15
16/// Manages multiple stages, each identified by a unique `StageTag`.
17///
18/// The `Stages` struct provides methods to add, retrieve, and modify stages.
19#[derive(Default, Debug)]
20pub struct Stages {
21    pub stages: HashMap<TypeId, Stage>,
22}
23
24impl Stages {
25    /// Creates a new, empty `Stages` instance.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use limnus_stage::Stages;
31    ///
32    /// let stages = Stages::new();
33    /// ```
34    #[must_use]
35    pub fn new() -> Self {
36        Self {
37            stages: HashMap::default(),
38        }
39    }
40
41    /// Adds a stage to the `Stages` collection with the specified tag.
42    ///
43    /// # Type Parameters
44    ///
45    /// - `S`: A type that implements the `StageTag` trait, used to uniquely identify the stage.
46    ///
47    /// # Parameters
48    ///
49    /// - `stage`: The `Stage` instance to be added.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// use limnus_stage::{Stages, Stage, StageTag};
55    ///
56    /// struct UpdateStage;
57    /// impl StageTag for UpdateStage {}
58    ///
59    /// let mut stages = Stages::new();
60    /// let stage = Stage::new();
61    /// stages.add::<UpdateStage>(stage);
62    /// ```
63    pub fn add<S>(&mut self, stage: Stage)
64    where
65        S: StageTag,
66    {
67        let stage_id = TypeId::of::<S>();
68        self.stages.insert(stage_id, stage);
69    }
70
71    /// Retrieves a mutable reference to a stage identified by the specified tag.
72    ///
73    /// # Type Parameters
74    ///
75    /// - `S`: A type that implements the `StageTag` trait, used to uniquely identify the stage.
76    ///
77    /// # Returns
78    ///
79    /// An `Option` containing a mutable reference to the `Stage` if it exists, or `None` otherwise.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use limnus_stage::{Stages, Stage, StageTag};
85    ///
86    /// struct UpdateStage;
87    /// impl StageTag for UpdateStage {}
88    ///
89    /// let mut stages = Stages::new();
90    /// let stage = Stage::new();
91    /// stages.add::<UpdateStage>(stage);
92    ///
93    /// if let Some(stage) = stages.get_mut::<UpdateStage>() {
94    ///     // Modify the stage
95    /// }
96    /// ```
97    #[must_use]
98    pub fn get_mut<S>(&mut self) -> Option<&mut Stage>
99    where
100        S: StageTag,
101    {
102        let stage_id = TypeId::of::<S>();
103        self.stages.get_mut(&stage_id)
104    }
105
106    /// Retrieves an immutable reference to a stage identified by the specified tag.
107    ///
108    /// # Type Parameters
109    ///
110    /// - `S`: A type that implements the `StageTag` trait, used to uniquely identify the stage.
111    ///
112    /// # Returns
113    ///
114    /// An `Option` containing a reference to the `Stage` if it exists, or `None` otherwise.
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// use limnus_stage::{Stages, Stage, StageTag};
120    ///
121    /// struct UpdateStage;
122    /// impl StageTag for UpdateStage {}
123    ///
124    /// let mut stages = Stages::new();
125    /// let stage = Stage::new();
126    /// stages.add::<UpdateStage>(stage);
127    ///
128    /// if let Some(stage) = stages.get::<UpdateStage>() {
129    ///     // Access the stage
130    /// }
131    /// ```
132    #[must_use]
133    pub fn get<S>(&self) -> Option<&Stage>
134    where
135        S: StageTag,
136    {
137        let stage_id = TypeId::of::<S>();
138        self.stages.get(&stage_id)
139    }
140
141    /// Retrieves an immutable reference to a stage by its `TypeId`.
142    ///
143    /// # Parameters
144    ///
145    /// - `stage_id`: A reference to the `TypeId` of the stage to retrieve.
146    ///
147    /// # Returns
148    ///
149    /// An `Option` containing a reference to the `Stage` if it exists, or `None` otherwise.
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// use limnus_stage::{Stages, Stage};
155    /// use std::any::TypeId;
156    ///
157    /// pub struct MyStageTag;
158    ///
159    /// let stages = Stages::new();
160    /// let stage_id = TypeId::of::<MyStageTag>();
161    /// if let Some(stage) = stages.get_by_id(&stage_id) {
162    ///     // Access the stage
163    /// }
164    /// ```
165    #[inline]
166    #[must_use]
167    pub fn get_by_id(&self, stage_id: &TypeId) -> Option<&Stage> {
168        self.stages.get(stage_id)
169    }
170}
171
172/// Represents a single stage containing a collection of systems.
173///
174/// A `Stage` can have multiple systems that execute with access to shared state.
175pub struct Stage {
176    systems: Vec<Box<dyn System>>,
177}
178
179impl Debug for Stage {
180    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
181        write!(f, "stage")
182    }
183}
184
185impl Default for Stage {
186    /// Creates a new `Stage` instance with no systems.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use limnus_stage::Stage;
192    ///
193    /// let stage = Stage::default();
194    /// ```
195    fn default() -> Self {
196        Self::new()
197    }
198}
199
200impl Stage {
201    /// Creates a new, empty `Stage` instance.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use limnus_stage::Stage;
207    ///
208    /// let stage = Stage::new();
209    /// ```
210    #[must_use]
211    pub fn new() -> Self {
212        Self { systems: vec![] }
213    }
214
215    /// Adds a system to the stage.
216    ///
217    /// Systems are the core units of logic that run within a stage. They are executed in the order they are added.
218    ///
219    /// # Type Parameters
220    ///
221    /// - `F`: A function or closure that can be converted into a `System`.
222    /// - `Params`: Parameters required by the system, implementing the `SystemParam` trait.
223    ///
224    /// # Parameters
225    ///
226    /// - `function`: The system function or closure to add.
227    ///
228    pub fn add_system<F, Params>(&mut self, function: F)
229    where
230        F: IntoSystem<Params>,
231        Params: SystemParam,
232    {
233        self.systems.push(Box::new(function.into_system()));
234    }
235
236    /// Executes all systems within the stage, providing mutable access to the shared `State`.
237    ///
238    /// Systems are run in the order they were added to the stage.
239    ///
240    /// # Parameters
241    ///
242    /// - `state`: A mutable reference to the shared `State` that systems can modify.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use limnus_stage::Stage;
248    /// use limnus_system_state::State;
249    ///
250    /// let stage = Stage::new();
251    /// let mut state = State::default();
252    /// stage.run(&mut state);
253    /// ```
254    pub fn run(&self, state: &mut State) {
255        for system in &self.systems {
256            system.run(state);
257        }
258    }
259}