github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/fsm/machine.go (about) 1 package main 2 3 import "fmt" 4 5 type Transition struct { 6 When State 7 And Event 8 Then State 9 Do func() 10 } 11 12 type State int 13 type Event int 14 15 type trigger struct { 16 In State 17 When Event 18 } 19 20 type action struct { 21 To State 22 Do func() 23 } 24 25 type Table []Transition 26 27 type Machine struct { 28 State State 29 Table Table 30 lookup map[trigger]action 31 } 32 33 func (m *Machine) init() { 34 m.lookup = make(map[trigger]action, len(m.Table)) 35 for _, t := range m.Table { 36 m.lookup[trigger{t.When, t.And}] = action{t.Then, t.Do} 37 } 38 } 39 40 func (m *Machine) Handle(e Event) { 41 if m.lookup == nil { 42 m.init() 43 } 44 action, ok := m.lookup[trigger{m.State, e}] 45 if !ok { 46 panic(fmt.Sprintf("entry missing for %v, %v", m.State, e)) 47 } 48 action.Do() 49 m.State = action.To 50 } 51 52 const ( 53 Locked = State(iota) 54 Unlocked 55 56 Coin = Event(iota) 57 Pass 58 ) 59 60 type Device interface { 61 Unlock() 62 Alarm() 63 ThankYou() 64 Lock() 65 } 66 67 func NewTurnstile(dev Device) *Machine { 68 return &Machine{ 69 State: Locked, 70 Table: Table{ 71 {Locked, Coin, Unlocked, dev.Unlock}, 72 {Locked, Pass, Locked, dev.Alarm}, 73 {Unlocked, Coin, Unlocked, dev.ThankYou}, 74 {Unlocked, Pass, Locked, dev.Lock}, 75 }} 76 } 77 78 type Printer struct{} 79 80 func (t Printer) Unlock() { fmt.Println("unlock") } 81 func (t Printer) Alarm() { fmt.Println("alarm") } 82 func (t Printer) ThankYou() { fmt.Println("thankyou") } 83 func (t Printer) Lock() { fmt.Println("lock") } 84 85 func main() { 86 m := NewTurnstile(Printer{}) 87 m.Handle(Coin) 88 m.Handle(Pass) 89 m.Handle(Pass) 90 m.Handle(Pass) 91 }