A mix code generator plugin (or perhaps a macro) will parse a PlantUML state machine diagram using nimble_parsec and output the following generated code:
defmodule Switch do
defmodule Switch.Switchable do
@callback flip_off_to_on_event(data :: Any.t()) :: Any.t()
@callback flip_on_to_off_event(data :: Any.t()) :: Any.t()
end
defmacro __using__(_) do
quote do
@behaviour Switch.Switchable
use GenStateMachine
def handle_event(:cast, :flip, :off, data) do
{:next_state, :on, __MODULE__.flip_off_to_on_event(data)}
end
def handle_event(:cast, :flip, :on, data) do
{:next_state, :off, __MODULE__.flip_off_to_on_event(data)}
end
def handle_event({:call, from}, :get_count, state, data) do
{:next_state, state, data, [{:reply, from, data}]}
end
def flip_off_to_on_event(data), do: data
def flip_on_to_off_event(data), do: data
defoverridable flip_off_to_on_event: 1
defoverridable flip_on_to_off_event: 1
end
end
end
The implementor will then override whichever event handlers they wish to implement - the event handler is responsible for mutating the data part of the state machines state.
defmodule SwitchImpl do
use Switch
@impl true
def flip_off_to_on_event(data), do: data + 1
@impl true
def flip_on_to_off_event(data), do: data
def start() do
GenStateMachine.start_link(SwitchImpl, {:off, 0})
end
def flip(pid), do: GenStateMachine.cast(pid, :flip)
def get_count(pid), do: GenStateMachine.call(pid, :get_count)
end
Example of usage :
iex(2)> {_,pid} = SwitchImpl.start
{:ok, #PID<0.279.0>}
iex(3)> SwitchImpl.flip(pid)
:ok
iex(4)> SwitchImpl.get_count(pid)
1
iex(5)> SwitchImpl.flip(pid)
:ok
iex(6)> SwitchImpl.get_count(pid)