defmodule Understated do alias Understated.Transition defmacro __using__(_opts) do quote do import Understated.Machine import Understated.State import Understated.Transition Module.register_attribute(__MODULE__, :transitions, accumulate: true) Module.register_attribute(__MODULE__, :states, accumulate: true) Module.register_attribute(__MODULE__, :current_state, accumulate: false) @current_state "" @before_compile unquote(__MODULE__) end end defmacro __before_compile__(%{module: module} = _env) do transitions = Module.get_attribute(module, :transitions, []) transitions_ast = transitions |> Enum.map(fn {state, to, _} -> quote do unquote(Transition.__compile_transitions(state, to)) end end) handlers_ast = transitions |> Enum.map(fn {state, to, on} -> quote do unquote(Transition.__compile_event_handlers(state, to, on)) end end) events_ast = transitions |> Enum.uniq_by(fn {_, _, on} -> on end) |> Enum.map(fn {_, _, on} -> quote do unquote(Transition.__compile_event(on)) end end) quote do def valid_states() do @states |> Enum.reverse() end def valid_transitions() do @transitions end unquote(transitions_ast) unquote(handlers_ast) unquote(events_ast) def transition(%__MODULE{} = machine, _to_state) do {:error, "Invalid transition", machine} end def trigger(%__MODULE{} = machine, _to_state) do {:error, "Invalid event for current state", machine} end end end end