defmodule TakeANumberDeluxe do alias TakeANumberDeluxe.State use GenServer # Client API @spec start_link(keyword()) :: {:ok, pid()} | {:error, atom()} def start_link(init_arg) do GenServer.start_link(__MODULE__, init_arg) end @spec report_state(pid()) :: State.t() def report_state(machine) do GenServer.call(machine, :report_state) end @spec queue_new_number(pid()) :: {:ok, integer()} | {:error, atom()} def queue_new_number(machine) do GenServer.call(machine, :queue_new_number) end @spec serve_next_queued_number(pid(), integer() | nil) :: {:ok, integer()} | {:error, atom()} def serve_next_queued_number(machine, priority_number \\ nil) do GenServer.call(machine, {:serve_next_queued_number, priority_number}) end @spec reset_state(pid()) :: :ok def reset_state(machine) do GenServer.cast(machine, :reset_state) end # Server callbacks @impl GenServer def init(options) do with min <- Keyword.get(options, :min_number), max <- Keyword.get(options, :max_number), shutdown <- Keyword.get(options, :auto_shutdown_timeout, :infinity), {:ok, state} <- State.new(min, max, shutdown) do {:ok, state, shutdown} else _ -> {:stop, :invalid_configuration} end end @impl GenServer def handle_call(:report_state, _from, state) do {:reply, state, state, state.auto_shutdown_timeout} end @impl GenServer def handle_call(:queue_new_number, _from, state) do case State.queue_new_number(state) do {:ok, new_number, new_state} -> {:reply, {:ok, new_number}, new_state, new_state.auto_shutdown_timeout} error -> {:reply, error, state, state.auto_shutdown_timeout} end end @impl GenServer def handle_call({:serve_next_queued_number, priority_number}, _from, state) do case State.serve_next_queued_number(state, priority_number) do {:ok, new_number, new_state} -> {:reply, {:ok, new_number}, new_state, new_state.auto_shutdown_timeout} error -> {:reply, error, state, state.auto_shutdown_timeout} end end @impl GenServer def handle_call(_request, _from, state) do {:reply, :unknown_command, state, state.auto_shutdown_timeout} end @impl GenServer def handle_cast( :reset_state, %State{min_number: min_number, max_number: max_number, auto_shutdown_timeout: auto_shutdown_timeout} ) do {:ok, new_state} = State.new(min_number, max_number, auto_shutdown_timeout) {:noreply, new_state, new_state.auto_shutdown_timeout} end @impl GenServer def handle_cast(_request, state) do {:noreply, state, state.auto_shutdown_timeout} end @impl GenServer def handle_info(:timeout, state) do {:stop, :normal, state} end @impl GenServer def handle_info(_request, state) do {:noreply, state, state.auto_shutdown_timeout} end end