Current section

Files

Jump to
tz_world lib tz_world backend memory.ex
Raw

lib/tz_world/backend/memory.ex

defmodule TzWorld.Backend.Memory do
@behaviour TzWorld.Backend
use GenServer
alias TzWorld.GeoData
alias Geo.Point
@timeout 10_000
@doc false
def start_link(options \\ []) do
GenServer.start_link(__MODULE__, options, name: __MODULE__)
end
@doc false
def stop(reason \\ :normal, timeout \\ :infinity) do
GenServer.stop(__MODULE__, reason, timeout)
end
@doc false
def init(_options) do
{:ok, [], {:continue, :load_data}}
end
@doc false
def version do
GenServer.call(__MODULE__, :version, @timeout)
end
@doc false
@spec timezone_at(Geo.Point.t()) :: {:ok, String.t()} | {:error, atom}
def timezone_at(%Point{} = point) do
GenServer.call(__MODULE__, {:timezone_at, point}, @timeout)
end
@doc false
@spec all_timezones_at(Geo.Point.t()) :: {:ok, [String.t()]} | {:error, atom}
def all_timezones_at(%Point{} = point) do
GenServer.call(__MODULE__, {:all_timezones_at, point}, @timeout)
end
@doc false
@spec reload_timezone_data :: {:ok, term}
def reload_timezone_data do
GenServer.call(__MODULE__, :reload_data, @timeout)
end
# --- Server callback implementation
@doc false
def handle_continue(:load_data, _state) do
case GeoData.load_compressed_data() do
{:ok, tz_data} -> {:noreply, tz_data}
other -> {:noreply, other}
end
end
@doc false
def handle_call(:reload_data, _from, _state) do
case GeoData.load_compressed_data() do
{:ok, tz_data} -> {:reply, {:ok, :loaded}, tz_data}
other -> {:reply, other, other}
end
end
@doc false
def handle_call(:version, _from, state) do
case state do
[version | _tz_data] -> {:reply, {:ok, version}, state}
other -> {:reply, other, state}
end
end
@doc false
def handle_call({:timezone_at, %Geo.Point{} = point}, _from, state) do
timezone =
with [_version | tz_data] <- state do
tz_data
|> Enum.filter(&TzWorld.contains?(&1.properties.bounding_box, point))
|> Enum.find(&TzWorld.contains?(&1, point))
|> case do
%Geo.MultiPolygon{properties: %{tzid: tzid}} -> {:ok, tzid}
%Geo.Polygon{properties: %{tzid: tzid}} -> {:ok, tzid}
nil -> {:error, :time_zone_not_found}
end
end
{:reply, timezone, state}
end
@doc false
def handle_call({:all_timezones_at, %Geo.Point{} = point}, _from, state) do
timezone =
with [_version | tz_data] <- state do
tz_data
|> Enum.filter(&TzWorld.contains?(&1.properties.bounding_box, point))
|> Enum.filter(&TzWorld.contains?(&1, point))
|> Enum.map(& &1.properties.tzid)
|> wrap(:ok)
end
{:reply, timezone, state}
end
defp wrap(term, atom) do
{atom, term}
end
end