farmbot_os/farmbot_core/lib/farmbot_core/dep_tracker/table.ex

69 lines
1.8 KiB
Elixir

defmodule FarmbotCore.DepTracker.Table do
use GenServer
@doc false
def start_link({table, _registry_name} = args) do
GenServer.start_link(__MODULE__, args, name: table)
end
@doc "put data"
def put(table, {identifier, status}) do
GenServer.call(table, {:put, identifier, status})
end
@doc "get data"
def get(table, {kind, _} = identifier) do
:ets.match(table, {identifier, :"$2"})
|> Enum.map(fn
[local_id, status] -> {{kind, local_id}, status}
other -> raise("unknown data in ets table: #{table} data: #{inspect(other)}")
end)
end
def get(table, service_name) do
:ets.match(table, {service_name, :"$2"})
|> Enum.map(fn
[status] -> {service_name, status}
other -> raise("unknown data in ets table: #{table} data: #{inspect(other)}")
end)
end
@impl GenServer
def init({table, registry_name}) do
^table = :ets.new(table, [:named_table, read_concurrency: true])
state = %{table: table, registry: registry_name}
{:ok, state}
end
@impl GenServer
def handle_call({:put, identifier, status}, _from, state) do
case :ets.lookup(state.table, identifier) do
[{^identifier, ^status}] ->
# No change, so no notifications
:ok
[{^identifier, old_status}] ->
:ets.insert(state.table, {identifier, status})
dispatch(state, identifier, old_status, status)
[] ->
:ets.insert(state.table, {identifier, status})
dispatch(state, identifier, nil, status)
end
{:reply, :ok, state}
end
defp dispatch(state, identifier, old, new) do
kind = case identifier do
{kind, _} -> kind
kind -> kind
end
Registry.dispatch(state.registry, kind, fn entries ->
message = {state.table, identifier, old, new}
for {pid, _} <- entries, do: send(pid, message)
end)
:ok
end
end