Simple State Machine and Transition Table
First there are a few things about design

Does you machine has something like 'error' that is not handled (e.g. by throwing an exception) or you have only 'outcomes' that handled in the same matter.

Do you expect branches in the FSM? Is it possible for an fsm method to fail? If possible how it is handled? E.g. ignored (say we rely on caller to retry on timer), raise failure event and queue it for execution, use return value as a guard to branch in state machine method.

Do you expect your code to maintained over 20+ years period?

After many attempts to make better existing FSMs, making my own, trying different frameworks and making my own frameworks too, I came to a trivially simple conclusion (aka 'dumb' FSM pattern): each event should be represented by a function and each function should contain a switch by state; or every state should be represented by a function and every function should contain a switch by event.

In the long run this is the cheapest solution. E.g. if you do not have branches/failure one can write a simple table driven fsm, however, once we got the first branch we need to either make it way more complicated or throw it away and rewrite using the 'dumb' approach