1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*!
# Topaz

Topaz is a tiny game engine. It uses [specs](https://crates.io/crates/specs)
for its ECS. Any frameworks for graphics, sound, UI, etc. are plugin
crates. All Topaz itself provides is a pushdown automaton (a state machine
coupled with a stack.)
*/

extern crate specs;

use std::rc::Rc;
use std::cell::RefCell;

/**
A transition to another `State`.
*/
pub enum Transition {
    ///Switch to another state.
    Switch(Rc<RefCell<State>>),
    ///Push another state.
    Push(Rc<RefCell<State>>),
    ///Pop this state, returning to the parent state.
    Quit,
    ///Quit the engine.
    Pop,
}

/**
All states must implement this trait.
*/
pub trait State {
    fn run(&mut self) -> Transition;
}

/**
The state manager.
*/
pub struct Engine {
    states: Vec<Rc<RefCell<State>>>,
}
impl Engine {
    pub fn new() -> Self {
        Self {
            states: Vec::new()
        }
    }
    
    /**
    Push a state onto the stack.
    */
    pub fn push_state(&mut self, state: Rc<RefCell<State>>) {
        self.states.push(state);
    }
    
    /**
    Run the `Engine`, starting with the state on the top of the stack.
    
    Since this function consumes the `Engine`, it cannot be reused.
    */
    pub fn run(mut self) {
        loop {
            if self.states.len() == 0 { return; }
            let state_cell = self.states[self.states.len()-1].clone();
            let mut state = state_cell.borrow_mut();
            match state.run() {
                Transition::Switch(new_state) => {
                    self.states.pop();
                    self.states.push(new_state);
                },
                Transition::Pop => {
                    self.states.pop();
                },
                Transition::Quit => {
                    return;
                },
                Transition::Push(new_state) => {
                    self.states.push(new_state);
                }
                
            }
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}