Current section
Files
Jump to
Current section
Files
lib/trip_switch.ex
defmodule TripSwitch do
@moduledoc """
Documentation for `TripSwitch`.
"""
use GenServer
alias TripSwitch.Circuit
@spec send(atom(), Circuit.signal()) :: {:ok, term()} | :broken
def send(id, signal) do
GenServer.call(via(id), {:send, signal})
end
@spec get(atom()) :: Circuit.t() | nil
def get(id) do
case Registry.lookup(TripSwitch.Registry, id) do
[] -> nil
[{pid, _}] -> GenServer.call(pid, :get)
end
end
@spec reset(atom()) :: :ok
def reset(id) do
GenServer.call(via(id), :reset)
end
@spec child_spec(keyword()) :: Supervisor.child_spec()
def child_spec(opts) do
unless name = opts[:name] do
raise ArgumentError, "expected :name option to be present"
end
Supervisor.child_spec(super(opts), id: {__MODULE__, name})
end
@impl GenServer
def init(opts), do: {:ok, %{circuit: Circuit.new(opts), repair: nil}}
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
name = Keyword.fetch!(opts, :name)
GenServer.start_link(__MODULE__, opts, name: via(name))
end
@impl GenServer
def handle_call(:get, _from, %{circuit: circuit} = state) do
{:reply, circuit, state}
end
def handle_call(:reset, _from, %{circuit: circuit} = state) do
{:reply, :ok, %{state | circuit: Circuit.reset(circuit)}}
end
def handle_call({:send, signal}, _from, %{circuit: circuit} = state) do
{result, circuit} = Circuit.handle(circuit, signal)
state = schedule_or_cancel_repair(circuit, state)
{:reply, result, state}
end
@impl GenServer
def handle_info(:repair, %{circuit: circuit} = state) do
state = schedule_or_cancel_repair(Circuit.repair(circuit), state)
{:noreply, state}
end
defp schedule_or_cancel_repair(
%Circuit{state: :open, fix_after: time} = circuit,
%{repair: nil} = state
)
when time > 0 do
timer = Process.send_after(self(), :repair, time)
%{state | circuit: circuit, repair: timer}
end
defp schedule_or_cancel_repair(
%Circuit{state: :open} = circuit,
%{repair: ref} = state
)
when is_reference(ref) do
%{state | circuit: circuit}
end
defp schedule_or_cancel_repair(%Circuit{} = circuit, state) do
case state.repair do
nil -> :ok
ref -> Process.cancel_timer(ref)
end
%{state | circuit: circuit, repair: nil}
end
defp via(id), do: {:via, Registry, {TripSwitch.Registry, id}}
end