Skip to main content

event_simulation/
lib.rs

1#![deny(missing_docs)]
2
3//! This crate provides a library for event based simulation of application state, particularly useful
4//! for representing progress and choices in video games or similar applications.
5//!
6//! The crate separates the simulation into two main components, the simulation info and the simulation
7//! state. The simulation info is immutable and defines the structure and rules of the simulation, while
8//! the simulation state is mutable and represents the current state of the simulation.
9//!
10//! The crate provides various types and traits to facilitate the interaction with the simulation, such
11//! as `Simulation`, `SimulationInfo`, `BorrowedSimulation`, `MultiSimulation`, and `OwnedSimulation`.
12//!
13//! For more detailed documentation and usage examples, please refer to the [crate documentation](https://docs.rs/event-simulation)
14//! or visit the [repository](https://gitlab.com/porky11/event-simulation).
15
16use std::ops::Deref;
17
18/// The `SimulationState` trait provides a read-only interface for inspecting the current state of a simulation.
19pub trait SimulationState {
20    /// The type used to access the current state data.
21    type AccessData: ?Sized;
22
23    /// The type of events that can be called or reverted in the simulation.
24    type Event: Copy + Ord;
25
26    /// The type of container used to access the available events.
27    type EventContainer<'a>: Iterator<Item = Self::Event>
28    where
29        Self: 'a;
30
31    /// Returns a reference to the data which represents the current state.
32    fn data(&self) -> &Self::AccessData;
33
34    /// Returns the events that can be called in the current state.
35    fn callables(&self) -> Self::EventContainer<'_>;
36
37    /// Returns the events that can be reverted in the current state.
38    fn revertables(&self) -> Self::EventContainer<'_>;
39
40    /// Checks if the provided event can be called in the current state.
41    fn callable(&self, event: Self::Event) -> bool;
42
43    /// Checks if the provided event can be reverted in the current state.
44    fn revertable(&self, event: Self::Event) -> bool;
45}
46
47/// The `Simulation` trait extends `SimulationState` by providing mutable access to modify the simulation state via events.
48pub trait Simulation: SimulationState {
49    /// The type of data used to load the simulation state.
50    type LoadData;
51
52    /// The error type returned when loading the simulation state fails.
53    type StateLoadingError;
54
55    /// Reloads the simulation state from the provided data.
56    ///
57    /// # Errors
58    /// Returns an error if a valid state cannot be loaded from `data`.
59    fn reload(&mut self, data: Self::LoadData) -> Result<(), Self::StateLoadingError>;
60
61    /// Calls the provided event.
62    ///
63    /// # Safety
64    ///
65    /// The caller must ensure that the event is valid and can be called in the current simulation state.
66    unsafe fn call(&mut self, event: Self::Event);
67
68    /// Reverts the provided event.
69    ///
70    /// # Safety
71    ///
72    /// The caller must ensure that the event is valid and can be reverted in the current simulation state.
73    unsafe fn revert(&mut self, event: Self::Event);
74
75    /// Tries to call the provided event and returns if it was successful.
76    fn try_call(&mut self, event: Self::Event) -> bool {
77        if !self.callable(event) {
78            return false;
79        }
80
81        unsafe { self.call(event) }
82
83        true
84    }
85
86    /// Tries to revert the provided event and returns if it was successful.
87    fn try_revert(&mut self, event: Self::Event) -> bool {
88        if !self.revertable(event) {
89            return false;
90        }
91
92        unsafe { self.revert(event) }
93
94        true
95    }
96
97    /// Prepares a safe helper to list callable elements and choose one to call.
98    fn prepare_call(&mut self) -> CallState<'_, Self, Call> {
99        let callables = self.callables().collect();
100        CallState {
101            simulation: self,
102            callables,
103            direction: Call,
104        }
105    }
106
107    /// Prepares a safe helper to list revertable elements and choose one to revert.
108    fn prepare_revert(&mut self) -> CallState<'_, Self, Revert> {
109        let callables = self.revertables().collect();
110        CallState {
111            simulation: self,
112            callables,
113            direction: Revert,
114        }
115    }
116}
117
118/// A trait representing a direction for calling or reverting events in a simulation.
119pub trait PlayDirection {
120    /// Calls the provided event based on the direction.
121    ///
122    /// # Safety
123    /// This method can assume the parameter to event to be callable.
124    unsafe fn call<S: Simulation>(&self, simulation: &mut S, event: S::Event);
125}
126
127/// A type representing the forward direction for calling events in a simulation.
128pub struct Call;
129impl PlayDirection for Call {
130    unsafe fn call<S: Simulation>(&self, simulation: &mut S, event: S::Event) {
131        unsafe { simulation.call(event) }
132    }
133}
134
135/// A type representing the backward direction for reverting events in a simulation.
136pub struct Revert;
137impl PlayDirection for Revert {
138    unsafe fn call<S: Simulation>(&self, simulation: &mut S, event: S::Event) {
139        unsafe { simulation.revert(event) }
140    }
141}
142
143/// A temporary object for selecting an event to call.
144pub struct CallState<'a, S: Simulation + ?Sized, D: PlayDirection> {
145    simulation: &'a mut S,
146    /// A slice of indices of the current callable events.
147    pub callables: Box<[S::Event]>,
148    direction: D,
149}
150
151impl<S: Simulation, D: PlayDirection> CallState<'_, S, D> {
152    /// Calls the event at the specified index.
153    ///
154    /// # Panics
155    /// Panics if `index` is out of bounds for `callables`.
156    /// Use [`Self::try_call`] for the non-panicking variant.
157    #[expect(clippy::indexing_slicing)]
158    pub fn call(self, index: usize) {
159        let event = self.callables[index];
160        unsafe { self.direction.call(self.simulation, event) }
161    }
162
163    /// Attempts to call the event at the specified index and returns if it was successful.
164    pub fn try_call(self, index: usize) -> bool {
165        let Some(&event) = self.callables.get(index) else {
166            return false;
167        };
168        unsafe { self.direction.call(self.simulation, event) }
169        true
170    }
171}
172
173/// The `SimulationInfo` trait provides an interface for interacting with a simulation.
174///
175/// # Safety
176/// This trait contains methods marked as `unsafe` that require careful usage.
177/// The following invariants must be upheld when calling these methods:
178///
179/// 1. The `state` parameter must be compatible with the current `SimulationInfo` instance.
180///    Implementations of this trait may assume that the provided `state` is compatible.
181/// 2. When calling `call` or `revert`, the `state` must be callable or revertable for the specified `event`.
182///
183/// Violating these invariants may lead to undefined behavior or incorrect simulation results.
184pub trait SimulationInfo {
185    /// The type of the simulation state.
186    type State;
187
188    /// The error type returned when loading the simulation state fails.
189    type StateLoadingError;
190
191    /// The type used to access the current state.
192    type AccessData: ?Sized;
193
194    /// The type of data used to load the simulation state.
195    type LoadData;
196
197    /// The type of events that can be called or reverted in the simulation.
198    type Event: Copy + Ord;
199
200    /// The type of container used to access the available events.
201    type EventContainer<'a>: Iterator<Item = Self::Event>
202    where
203        Self: 'a;
204
205    /// Creates a new default state compatible with this `SimulationInfo` instance.
206    fn default_state(&self) -> Self::State;
207
208    /// Loads a state from the provided data, returning a `Result` with the loaded state or an error.
209    ///
210    /// # Errors
211    /// Returns an error if a valid state cannot be loaded from `data`.
212    fn load_state(&self, data: Self::LoadData) -> Result<Self::State, Self::StateLoadingError>;
213
214    /// Clones the provided `state`, assuming it is compatible with this `SimulationInfo` instance.
215    ///
216    /// # Safety
217    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance.
218    unsafe fn clone_state(&self, state: &Self::State) -> Self::State;
219
220    /// Returns a reference to the data which repsesents the `state`.
221    ///
222    /// # Safety
223    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance.
224    unsafe fn data<'a>(&self, state: &'a Self::State) -> &'a Self::AccessData;
225
226    /// Returns the events that can be called for the provided `state`.
227    fn callables(state: &Self::State) -> Self::EventContainer<'_>;
228
229    /// Returns the events that can be reverted for the provided `state`.
230    fn revertables(state: &Self::State) -> Self::EventContainer<'_>;
231
232    /// Checks if the provided `event` can be called for the given `state`.
233    fn callable(state: &Self::State, event: Self::Event) -> bool;
234
235    /// Checks if the provided `event` can be reverted for the given `state`.
236    fn revertable(state: &Self::State, event: Self::Event) -> bool;
237
238    /// Calls the provided `event` on the given mutable `state`.
239    ///
240    /// # Safety
241    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance
242    /// and that the `state` is callable for the specified `event`.
243    unsafe fn call(&self, state: &mut Self::State, event: Self::Event);
244
245    /// Reverts the provided `event` on the given mutable `state`.
246    ///
247    /// # Safety
248    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance
249    /// and that the `state` is revertable for the specified `event`.
250    unsafe fn revert(&self, state: &mut Self::State, event: Self::Event);
251}
252
253/// The `EditalbeSimulationInfo` trait provides an interface for editing the simulation while ensuring the state to stay valid.
254pub trait EditableSimulationInfo: SimulationInfo {
255    /// The type used for safe edits.
256    type Edit<'a>: Deref<Target = Self>
257    where
258        Self: 'a;
259
260    /// Creates a type which allows safe edits to the info without invalidating the states.
261    ///
262    /// # Safety
263    /// After editing the info using the edit type, `refresh_state` has to be called before continuing the simulation.
264    unsafe fn edit(&mut self) -> Self::Edit<'_>;
265
266    /// Refreshes the provided mutable `state`, assuming it is compatible with this `SimulationInfo` instance.
267    ///
268    /// # Safety
269    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance.
270    unsafe fn refresh_state(&self, state: &mut Self::State);
271}
272
273/// A trait for types that can be safely edited without invalidating their associated states.
274pub trait Editable {
275    /// The type used for safe edits and refreshing the state.
276    type Edit<'a>
277    where
278        Self: 'a;
279
280    /// Creates a type which allows safe edits to the info without invalidating the states,
281    /// and automatically refreshes the states when the edit type goes out of scope.
282    fn edit(&mut self) -> Self::Edit<'_>;
283}
284
285mod borrowed;
286mod multi;
287mod owned;
288
289pub use borrowed::BorrowedSimulation;
290pub use multi::{
291    MultiSimulation, MultiSimulationEdit, SimulationBorrow, SimulationBorrowMut,
292    SimulationBorrowRef,
293};
294pub use owned::{OwnedSimulation, OwnedSimulationEdit};
295
296mod dynamic;
297
298pub use dynamic::DynamicSimulation;