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  }