event-simulation 0.2.0

A library for event based simulation of application state
Documentation
#![deny(missing_docs)]

//! This crate provides a library for event based simulation of application state, particularly useful
//! for representing progress and choices in video games or similar applications.
//!
//! The crate separates the simulation into two main components, the simulation info and the simulation
//! state. The simulation info is immutable and defines the structure and rules of the simulation, while
//! the simulation state is mutable and represents the current state of the simulation.
//!
//! The crate provides various types and traits to facilitate the interaction with the simulation, such
//! as `Simulation`, `SimulationInfo`, `BorrowedSimulation`, `MultiSimulation`, and `OwnedSimulation`.
//!
//! For more detailed documentation and usage examples, please refer to the [crate documentation](https://docs.rs/event-simulation)
//! or visit the [repository](https://gitlab.com/porky11/event-simulation).

use std::ops::Deref;

/// The `SimulationState` trait provides a read-only interface for inspecting the current state of a simulation.
pub trait SimulationState {
    /// The type used to access the current state data.
    type AccessData: ?Sized;

    /// The type of events that can be called or reverted in the simulation.
    type Event: Copy + Ord;

    /// The type of container used to access the available events.
    type EventContainer<'a>: Iterator<Item = Self::Event>
    where
        Self: 'a;

    /// Returns a reference to the data which represents the current state.
    fn data(&self) -> &Self::AccessData;

    /// Returns the events that can be called in the current state.
    fn callables(&self) -> Self::EventContainer<'_>;

    /// Returns the events that can be reverted in the current state.
    fn revertables(&self) -> Self::EventContainer<'_>;

    /// Checks if the provided event can be called in the current state.
    fn callable(&self, event: Self::Event) -> bool;

    /// Checks if the provided event can be reverted in the current state.
    fn revertable(&self, event: Self::Event) -> bool;
}

/// The `Simulation` trait extends `SimulationState` by providing mutable access to modify the simulation state via events.
pub trait Simulation: SimulationState {
    /// The type of data used to load the simulation state.
    type LoadData;

    /// The error type returned when loading the simulation state fails.
    type StateLoadingError;

    /// Reloads the simulation state from the provided data.
    fn reload(&mut self, data: Self::LoadData) -> Result<(), Self::StateLoadingError>;

    /// Calls the provided event.
    ///
    /// # Safety
    ///
    /// The caller must ensure that the event is valid and can be called in the current simulation state.
    unsafe fn call(&mut self, event: Self::Event);

    /// Reverts the provided event.
    ///
    /// # Safety
    ///
    /// The caller must ensure that the event is valid and can be reverted in the current simulation state.
    unsafe fn revert(&mut self, event: Self::Event);

    /// Tries to call the provided event and returns if it was successful.
    fn try_call(&mut self, event: Self::Event) -> bool {
        if !self.callable(event) {
            return false;
        }

        unsafe { self.call(event) }

        true
    }

    /// Tries to revert the provided event and returns if it was successful.
    fn try_revert(&mut self, event: Self::Event) -> bool {
        if !self.revertable(event) {
            return false;
        }

        unsafe { self.revert(event) }

        true
    }

    /// Prepares a safe helper to list callable elements and choose one to call.
    fn prepare_call(&mut self) -> CallState<'_, Self, Call> {
        let callables = self.callables().collect();
        CallState {
            simulation: self,
            callables,
            direction: Call,
        }
    }

    /// Prepares a safe helper to list revertable elements and choose one to revert.
    fn prepare_revert(&mut self) -> CallState<'_, Self, Revert> {
        let callables = self.revertables().collect();
        CallState {
            simulation: self,
            callables,
            direction: Revert,
        }
    }
}

/// A trait representing a direction for calling or reverting events in a simulation.
pub trait PlayDirection {
    /// Calls the provided event based on the direction.
    ///
    /// # Safety
    /// This method can assume the parameter to event to be callable.
    unsafe fn call<S: Simulation>(&self, simulation: &mut S, event: S::Event);
}

/// A type representing the forward direction for calling events in a simulation.
pub struct Call;
impl PlayDirection for Call {
    unsafe fn call<S: Simulation>(&self, simulation: &mut S, event: S::Event) {
        unsafe { simulation.call(event) }
    }
}

/// A type representing the backward direction for reverting events in a simulation.
pub struct Revert;
impl PlayDirection for Revert {
    unsafe fn call<S: Simulation>(&self, simulation: &mut S, event: S::Event) {
        unsafe { simulation.revert(event) }
    }
}

/// A temporary object for selecting an event to call.
pub struct CallState<'a, S: Simulation + ?Sized, D: PlayDirection> {
    simulation: &'a mut S,
    /// A slice of indices of the current callable events.
    pub callables: Box<[S::Event]>,
    direction: D,
}

impl<S: Simulation, D: PlayDirection> CallState<'_, S, D> {
    /// Calls the event at the specified index.
    pub fn call(self, index: usize) {
        let event = self.callables[index];
        unsafe { self.direction.call(self.simulation, event) }
    }

    /// Attempts to call the event at the specified index and returns if it was successful.
    pub fn try_call(self, index: usize) -> bool {
        let Some(&event) = self.callables.get(index) else {
            return false;
        };
        unsafe { self.direction.call(self.simulation, event) }
        true
    }
}

/// The `SimulationInfo` trait provides an interface for interacting with a simulation.
///
/// # Safety
/// This trait contains methods marked as `unsafe` that require careful usage.
/// The following invariants must be upheld when calling these methods:
///
/// 1. The `state` parameter must be compatible with the current `SimulationInfo` instance.
///    Implementations of this trait may assume that the provided `state` is compatible.
/// 2. When calling `call` or `revert`, the `state` must be callable or revertable for the specified `event`.
///
/// Violating these invariants may lead to undefined behavior or incorrect simulation results.
pub trait SimulationInfo {
    /// The type of the simulation state.
    type State;

    /// The error type returned when loading the simulation state fails.
    type StateLoadingError;

    /// The type used to access the current state.
    type AccessData: ?Sized;

    /// The type of data used to load the simulation state.
    type LoadData;

    /// The type of events that can be called or reverted in the simulation.
    type Event: Copy + Ord;

    /// The type of container used to access the available events.
    type EventContainer<'a>: Iterator<Item = Self::Event>
    where
        Self: 'a;

    /// Creates a new default state compatible with this `SimulationInfo` instance.
    fn default_state(&self) -> Self::State;

    /// Loads a state from the provided data, returning a `Result` with the loaded state or an error.
    fn load_state(&self, data: Self::LoadData) -> Result<Self::State, Self::StateLoadingError>;

    /// Clones the provided `state`, assuming it is compatible with this `SimulationInfo` instance.
    ///
    /// # Safety
    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance.
    unsafe fn clone_state(&self, state: &Self::State) -> Self::State;

    /// Returns a reference to the data which repsesents the `state`.
    ///
    /// # Safety
    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance.
    unsafe fn data<'a>(&self, state: &'a Self::State) -> &'a Self::AccessData;

    /// Returns the events that can be called for the provided `state`.
    fn callables(state: &Self::State) -> Self::EventContainer<'_>;

    /// Returns the events that can be reverted for the provided `state`.
    fn revertables(state: &Self::State) -> Self::EventContainer<'_>;

    /// Checks if the provided `event` can be called for the given `state`.
    fn callable(state: &Self::State, event: Self::Event) -> bool;

    /// Checks if the provided `event` can be reverted for the given `state`.
    fn revertable(state: &Self::State, event: Self::Event) -> bool;

    /// Calls the provided `event` on the given mutable `state`.
    ///
    /// # Safety
    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance
    /// and that the `state` is callable for the specified `event`.
    unsafe fn call(&self, state: &mut Self::State, event: Self::Event);

    /// Reverts the provided `event` on the given mutable `state`.
    ///
    /// # Safety
    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance
    /// and that the `state` is revertable for the specified `event`.
    unsafe fn revert(&self, state: &mut Self::State, event: Self::Event);
}

/// The `EditalbeSimulationInfo` trait provides an interface for editing the simulation while ensuring the state to stay valid.
pub trait EditableSimulationInfo: SimulationInfo {
    /// The type used for safe edits.
    type Edit<'a>: Deref<Target = Self>
    where
        Self: 'a;

    /// Creates a type which allows safe edits to the info without invalidating the states.
    ///
    /// # Safety
    /// After editing the info using the edit type, `refresh_state` has to be called before continuing the simulation.
    unsafe fn edit(&mut self) -> Self::Edit<'_>;

    /// Refreshes the provided mutable `state`, assuming it is compatible with this `SimulationInfo` instance.
    ///
    /// # Safety
    /// The caller must ensure that the provided `state` is compatible with this `SimulationInfo` instance.
    unsafe fn refresh_state(&self, state: &mut Self::State);
}

/// A trait for types that can be safely edited without invalidating their associated states.
pub trait Editable {
    /// The type used for safe edits and refreshing the state.
    type Edit<'a>
    where
        Self: 'a;

    /// Creates a type which allows safe edits to the info without invalidating the states,
    /// and automatically refreshes the states when the edit type goes out of scope.
    fn edit(&mut self) -> Self::Edit<'_>;
}

mod borrowed;
mod multi;
mod owned;

pub use borrowed::BorrowedSimulation;
pub use multi::{
    MultiSimulation, MultiSimulationEdit, SimulationBorrow, SimulationBorrowMut,
    SimulationBorrowRef,
};
pub use owned::{OwnedSimulation, OwnedSimulationEdit};

mod dynamic;

pub use dynamic::DynamicSimulation;