defmodule Understated do alias Understated.Transition defmacro __using__(_options) 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, _on} -> 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 {_state, _to, 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 before_event(machine, _from, _to), do: machine def after_event(machine, _from, _to), do: machine def trigger(%__MODULE{} = machine, _to_state) do {:error, "Invalid event for current state", machine} end defp apply_state(%__MODULE__{} = machine, _state) do {:error, "Invalid transition", machine} end end end end