solstack 0.3.0

Enables management of your app's or game's control flow through a simple state stack machine.
Documentation
use crate::trans::Trans;
use crate::state::BoxState;

/// Handles the control flow of a stack of [`State`](crate::state::States).
/// Essentialy the `State Stack Machine`.
///
/// See the crates docs or the book for more information.
/// ### Generics
/// - D: Data available to all [`State`s](crate::state::State) to perform writes and reads to.
#[derive(Default)]
pub struct Stack<D> {

    stack: Vec<BoxState<D>>,
}

// Constructors
impl <D> Stack<D> {

    /// New empty stack state machine; a.k.a. [`Stack`].
    #[must_use]
    pub fn new() -> Self { Self { stack: vec![] } }
}

// Utilities
impl <D> Stack<D> {

    /// Checks if there is at least one state on the stack.
    #[must_use]
    pub fn is_running(&self) -> bool { !self.stack.is_empty() }

    /// Get the length of the stack.
    #[must_use]
    pub fn len(&self) -> usize {
        self.stack.len()
    }

    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.stack.is_empty()
    }
}

// Pure transitions.
impl <D> Stack<D> {

    /// Pushes a new state above the current one.
    /// Effectively pauses the current state until everything above it is
    /// popped.
    pub fn push(&mut self, data: &mut D, mut state: BoxState<D>) {
        // Run current `State`'s `.on_pause()`.
        if let Some(curr_state) = self.stack.last_mut() {
            curr_state.on_pause(data);
        }

        // Starts the new state.
        state.on_start(data);
        // Pushes the new state onto the stack.
        self.stack.push(state);
    }

    /// Pops the current [`State`](crate::state::State) from the stack.
    pub fn pop(&mut self, data: &mut D) {
        // Runs this `State`'s `.on_stop()`.
        // Also pops it from the stack (rhs of if-let).
        if let Some(mut curr_state) = self.stack.pop() {
            curr_state.on_stop(data);
        }

        // Runs the `State` below's `.on_resume()`.
        if let Some(state) = self.stack.last_mut() {
            state.on_resume(data);
        }
    }
    
    /// Pops everything from the stack.
    /// Effectively ends the state stack machine.
    pub fn quit(&mut self, data: &mut D) {
        // Pops everything out of the stack.
        // Also calls `.on_stop()` for all of the stack (rhs of while-let).
        while let Some(mut state) = self.stack.pop() { state.on_stop(data); }
    }
    
}

// Composed transitions.
impl <D> Stack<D> {
    /// Pops and pushes a new state.
    /// Effectively replaces the current state with a new one.
    pub fn replace(&mut self, data: &mut D, state: BoxState<D>) {
        self.pop(data);
        self.push(data, state);
    }    

    /// Pops every state from the stack and pushes a new one.
    /// Effectively isolates it as the only state on the stack.
    pub fn isolate(&mut self, data: &mut D, state: BoxState<D>) {
        self.quit(data);
        self.push(data, state);
    }    
}

impl <D> Stack<D> {
    
    /// Ticks the current (topmost at the stack) state once.
    pub fn tick(&mut self, data: &mut D) {
        // ticks all of the state's `on_shadow_update` and store the
        // transitions.
        let mut shadow_transitions: Vec<Trans<D>> = vec![];

        for state in self.stack.iter_mut() {
            shadow_transitions.push(state.on_shadow_tick(data));
        };

        // perform the transitions of all of the states on `on_shadow_tick`.
        for shadow_transition in shadow_transitions {
            match shadow_transition {
                Trans::Push(state)    => self.push   (data, state),
                Trans::Pop            => self.pop    (data       ),
                Trans::Replace(state) => self.replace(data, state),
                Trans::Isolate(state) => self.isolate(data, state),
                Trans::None           =>             (           ),
                Trans::Quit           => self.quit   (data       ),
            }
        }

        // ticks the topmost state at the stack.
        let transition: Trans<D> = match self.stack.last_mut() {
            Some(state) => state.on_tick(data),
            None => Trans::None,
        };

        // perform the transition returned by the topmost stack's `on_tick`.
        match transition {
            Trans::Push(state)    => self.push   (data, state),
            Trans::Pop            => self.pop    (data       ),
            Trans::Replace(state) => self.replace(data, state),
            Trans::Isolate(state) => self.isolate(data, state),
            Trans::None           =>             (           ),
            Trans::Quit           => self.quit   (data       ),
        };
    }
}