diff options
author | Vincent Ambo <mail@tazj.in> | 2018-09-26T09·36+0200 |
---|---|---|
committer | Vincent Ambo <mail@tazj.in> | 2018-09-26T14·54+0200 |
commit | 60824a06f1db981a07d738c71ba65c965a473838 (patch) | |
tree | 41e493c4edf16c0fa0c7b76f16da9d06af5edf26 /finito-door/src/lib.rs | |
parent | da66599696dce1378e6fcef6a5149ba60e5006a2 (diff) |
feat(door): Check in example door implementation
Checks in my classic, lockable door example implemented in Finito. This does not yet contain the documentation of the door in the Haskell version of Finito.
Diffstat (limited to 'finito-door/src/lib.rs')
-rw-r--r-- | finito-door/src/lib.rs | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/finito-door/src/lib.rs b/finito-door/src/lib.rs new file mode 100644 index 000000000000..a4aad8dcbbe3 --- /dev/null +++ b/finito-door/src/lib.rs @@ -0,0 +1,144 @@ +//! TODO: port the door docs + +extern crate finito; + +use finito::FSM; + +type Code = usize; +type Attempts = usize; + +#[derive(Debug, PartialEq)] +pub enum DoorState { + /// This state represents an open door. + Opened, + + /// This state represents a closed door. + Closed, + + /// This state represents a locked door on which a given code + /// is set. It also carries a number of remaining attempts + /// before the door is permanently disabled. + Locked { code: Code, attempts: Attempts }, + + /// This state represents a disabled door. The police will + /// need to unlock it manually! + Disabled, +} + +#[derive(Debug)] +pub enum DoorEvent { + Open, + Close, + Lock(Code), + Unlock(Code), +} + +#[derive(Debug, PartialEq)] +pub enum DoorAction { + NotifyIRC(String), + CallThePolice, +} + +impl FSM for DoorState { + type Event = DoorEvent; + type Action = DoorAction; + + fn handle(self, event: DoorEvent) -> (Self, Vec<DoorAction>) { + match (self, event) { + (DoorState::Opened, DoorEvent::Close) => return (DoorState::Closed, vec![]), + + (DoorState::Closed, DoorEvent::Open) => return (DoorState::Opened, vec![]), + + (DoorState::Closed, DoorEvent::Lock(code)) => { + return (DoorState::Locked { code, attempts: 3 }, vec![]) + } + + (DoorState::Locked { code, attempts }, DoorEvent::Unlock(unlock_code)) => { + if code == unlock_code { + return (DoorState::Closed, vec![]); + } + + if attempts == 1 { + return (DoorState::Disabled, vec![DoorAction::CallThePolice]); + } + + return ( + DoorState::Locked { + code, + attempts: attempts - 1, + }, + vec![DoorAction::NotifyIRC("invalid code entered".into())], + ); + } + + (current, _) => (current, vec![]), + } + } + + fn enter(&self) -> Vec<DoorAction> { + let msg = match self { + DoorState::Opened => "door was opened", + DoorState::Closed => "door was closed", + DoorState::Locked { .. } => "door was locked", + DoorState::Disabled => "door was disabled", + }; + + vec![DoorAction::NotifyIRC(msg.into())] + } + + fn act(action: DoorAction) -> Vec<DoorEvent> { + match action { + DoorAction::NotifyIRC(msg) => { + // TODO: write to file in example + println!("IRC: {}", msg); + vec![] + } + + DoorAction::CallThePolice => { + // TODO: call the police + println!("The police was called! For real!"); + vec![] + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use finito::advance; + + fn test_fsm<S: FSM>(initial: S, events: Vec<S::Event>) -> (S, Vec<S::Action>) { + events.into_iter().fold((initial, vec![]), |(state, mut actions), event| { + let (new_state, mut new_actions) = advance(state, event); + actions.append(&mut new_actions); + (new_state, actions) + }) + } + + #[test] + fn test_door() { + let initial = DoorState::Opened; + let events = vec![ + DoorEvent::Close, + DoorEvent::Open, + DoorEvent::Close, + DoorEvent::Lock(1234), + DoorEvent::Unlock(1234), + DoorEvent::Lock(4567), + DoorEvent::Unlock(1234), + ]; + let (final_state, actions) = test_fsm(initial, events); + + assert_eq!(final_state, DoorState::Locked { code: 4567, attempts: 2 }); + assert_eq!(actions, vec![ + DoorAction::NotifyIRC("door was closed".into()), + DoorAction::NotifyIRC("door was opened".into()), + DoorAction::NotifyIRC("door was closed".into()), + DoorAction::NotifyIRC("door was locked".into()), + DoorAction::NotifyIRC("door was closed".into()), + DoorAction::NotifyIRC("door was locked".into()), + DoorAction::NotifyIRC("invalid code entered".into()), + ]); + } +} |