vegapokerbot/pkg/fsm/state.go

84 lines
3.6 KiB
Go
Raw Normal View History

2024-05-13 16:57:55 +03:00
package fsm
// StateID is a state identifier. Machine with string state IDs can have *a lot of* states.
type StateID string
// NilStateID is a noop state. Machine won't do anything for this transition.
const NilStateID = StateID("")
// MachineControls is a fragment of IMachine implementation. This one can Move between the states or Reset the machine.
// It may fail with an error which should be handled by the IState implementation.
type MachineControls[T any] interface {
2024-05-14 14:44:41 +03:00
Move(StateID, *T) error
2024-05-13 16:57:55 +03:00
Reset()
}
2024-05-14 14:44:41 +03:00
// MachineState returns machine state. This state should be immutable.
type MachineState[T any] interface {
// State returns underlying state. This state SHOULD NOT be modified since there is no locking performed.
State() *T
}
// MachineControlsWithState is self-explanatory.
type MachineControlsWithState[T any] interface {
MachineControls[T]
MachineState[T]
}
2024-05-13 16:57:55 +03:00
// IState is a State interface. This contract enforces that any state implementation should have three methods:
// Enter, Handle and Exit. The first one is called right after the machine has moved to the state, the second one is
// called when handling some state input, the last one is called right before leaving to a next state (which happens
// if Handle has called Move on MachineControls.
type IState[T any] interface {
// ID should return state identifier.
ID() StateID
// Enter is a state enter callback. Can be used to perform some sort of input query or to move to another
// state immediately. Also, if Enter fails the machine won't move to the state and MachineControls's Move will
// return an error which was returned from Enter.
2024-05-14 14:44:41 +03:00
Enter(*T, MachineControls[T]) error
2024-05-13 16:57:55 +03:00
// Handle is called when receiving some sort of input response. This one's signature is nearly identical to Enter,
// but it can wait for some sort of input (standard input? webhook) without locking inside the callback
// while Enter cannot do that.
2024-05-14 14:44:41 +03:00
Handle(*T, MachineControls[T])
2024-05-13 16:57:55 +03:00
// Exit is called right before leaving the state. It can be used to modify state's payload (T) or for some other
// miscellaneous tasks.
// Note: calling Exit doesn't mean that the machine will really transition to the next state.
// The next state's Enter callback can return an error which will reset the Machine to default state & payload.
Exit(*T)
}
// ErrorState is the Machine's fatal error handler which you should implement yourself.
//
// Error state is used by Machine in case of a fatal error (for example, for invalid state ID).
// You can use ErrorState to make some kind of fatal error response like "Internal error has occurred"
// or something similar. Also, ErrorState is special because neither Enter nor Exit exists in it.
//
// Machine without ErrorState will not do anything in case of fatal errors.
type ErrorState[T any] interface {
2024-05-14 14:44:41 +03:00
Handle(err error, current StateID, next StateID, payload *T, machine MachineControls[T])
2024-05-13 16:57:55 +03:00
}
// State is the Machine's state. This implementation doesn't do anything and only helps with the
// actual IState implementation (you don't need to write empty Enter and Exit callback if you don't use them).
type State[T any] struct {
Payload *T
}
// ID panics because you need to implement it.
func (s *State[T]) ID() StateID {
panic("implement ID() StateID method for your state")
}
// Enter here will immediately move the Machine to empty state.
2024-05-14 14:44:41 +03:00
func (s *State[T]) Enter(payload *T, machine MachineControls[T]) error {
2024-05-13 16:57:55 +03:00
return machine.Move(NilStateID, payload)
}
// Handle here will immediately move the Machine to empty state.
2024-05-14 14:44:41 +03:00
func (s *State[T]) Handle(payload *T, machine MachineControls[T]) {
2024-05-13 16:57:55 +03:00
_ = machine.Move(NilStateID, payload)
}
// Exit won't do anything.
func (s *State[T]) Exit(*T) {}