From 60824a06f1db981a07d738c71ba65c965a473838 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 26 Sep 2018 11:36:08 +0200 Subject: 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. --- Cargo.toml | 3 +- finito-door/Cargo.toml | 7 +++ finito-door/src/lib.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 finito-door/Cargo.toml create mode 100644 finito-door/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index bb5cb6b67094..5d62cea6ec2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ - "finito-core" + "finito-core", + "finito-door" ] diff --git a/finito-door/Cargo.toml b/finito-door/Cargo.toml new file mode 100644 index 000000000000..3497a1c04cca --- /dev/null +++ b/finito-door/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "finito-door" +version = "0.1.0" +authors = ["Vincent Ambo "] + +[dependencies.finito] +path = "../finito-core" 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) { + 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 { + 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 { + 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(initial: S, events: Vec) -> (S, Vec) { + 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()), + ]); + } +} -- cgit 1.4.1