package fsm import ( "errors" "fmt" "io" "log" ) type ( // ErrorString is a specific error type used to recognize FSM errors. ErrorString string // OnError is the type of FSM optional error handlers. OnError[SK, EK comparable] func(SK, EK, error) error ) const ( ErrUnavailableEvent ErrorString = "unavailable event" ErrGuardFailure ErrorString = "guard failure" ErrActionFailure ErrorString = "action failure" ErrEnterFailure ErrorString = "enter failure" ErrLeaveFailure ErrorString = "leave failure" ) func (e ErrorString) Error() string { return string(e) } func MakeFailureError[SK, EK comparable](sk SK, ek EK, err error) error { var es ErrorString if ok := errors.As(err, &es); !ok { return fmt.Errorf("generic error at state %s / event %s: %w", sk, ek, err) } var format string switch es { case ErrUnavailableEvent: format = "unavailable event %s received on state %s: %w" case ErrGuardFailure: format = "guard failed for event %s received on state %s: %w" case ErrActionFailure: format = "action failed after event %s received on state %s: %w" case ErrEnterFailure: format = "enter failed entering state %s after event %s: %w" case ErrLeaveFailure: format = "leave failed leaving state %s on event %s: %w" default: format = "custom error at state %s / event %s: %w" } return fmt.Errorf(format, sk, ek, es) } // OnErrorIgnore silently ignores errors. Usually not recommended: mostly useful in tests. func OnErrorIgnore[SK, EK comparable](_ SK, _ EK, _ error) error { return nil } // OnErrorReturn returns the received error and, allowing the FSM to act on it. func OnErrorReturn[SK, EK comparable](sk SK, ek EK, err error) error { return MakeFailureError(sk, ek, err) } // OnErrorPanic panics when receiving an error. Not recommended. func OnErrorPanic[SK, EK comparable](sk SK, ek EK, err error) error { panic(MakeFailureError(sk, ek, err)) } // MakeOnErrorWrite logs the error to the specified writer and returns success. func MakeOnErrorWrite[SK, EK comparable](w io.Writer) OnError[SK, EK] { return func(sk SK, ek EK, err error) error { fmt.Fprintln(w, MakeFailureError(sk, ek, err)) return nil } } // _ is a compile-time verification of the type conformity for OnError handlers func _() { var fsm FSM[string, string, string] fsm.SetOnUnavailable(OnErrorIgnore[string, string]) fsm.SetOnUnavailable(OnErrorReturn[string, string]) fsm.SetOnUnavailable(OnErrorPanic[string, string]) fsm.SetOnUnavailable(MakeOnErrorWrite[string, string](log.Default().Writer())) }