Current section
Files
Jump to
Current section
Files
lib/mogs/timers/store.ex
defprotocol Mogs.Timers.Store do
# Mogs types
@type t :: term
@type timers :: Mogs.Timers.t()
@fallback_to_any true
@spec get_timers(t) :: timers
def get_timers(board)
@spec put_timers(t, timers) :: t
def put_timers(board, timers)
end
defimpl Mogs.Timers.Store, for: Any do
defmacro __deriving__(module, struct, options) do
case find_key(struct, options) do
{:raise, exception, message} ->
quote do
reraise unquote(exception),
[message: unquote(message)],
unquote(Macro.escape(Macro.Env.stacktrace(__CALLER__)))
end
{:ok, key} ->
quote location: :keep do
defimpl unquote(@protocol), for: unquote(module) do
@type timers :: Mogs.Timers.t()
@type impl :: %{:__struct__ => unquote(module), unquote(key) => timers}
@spec get_timers(impl) :: timers
def get_timers(board) do
Map.fetch!(board, unquote(key))
end
@spec put_timers(impl, timers) :: impl
def put_timers(board, timers) do
Map.put(board, unquote(key), timers)
end
end
end
end
end
defp raise_unimplemented(%name{} = struct) do
raise Protocol.UndefinedError,
protocol: @protocol,
value: struct,
description: """
#{@protocol} protocol must always be explicitly implemented.
If you own the struct, you can derive the implementation specifying \
the key where timers must be stored:
defmodule #{inspect(name)} do
@derive {#{inspect(@protocol)}, key: timers}
defstruct timers: Mogs.Timers.new()
If you don't own the struct you may use Protocol.derive/3 placed outside \
of any module:
Protocol.derive(#{inspect(@protocol)}, #{inspect(name)}, key: :timers)
"""
end
defp raise_unimplemented(value) do
raise Protocol.UndefinedError,
protocol: @protocol,
value: value,
description: "#{@protocol} protocol must always be explicitly implemented"
end
def get_timers(value) do
# Dialyzer trick
case :erlang.phash2(1, 1) do
0 -> raise_unimplemented(value)
# This code will NEVER be executed
1 -> TimeQueue.new()
end
end
def put_timers(value, _) do
# Dialyzer trick
case :erlang.phash2(1, 1) do
0 -> raise_unimplemented(value)
# This code will NEVER be executed
1 -> value
end
end
defp find_key(%{__struct__: name} = struct, key) when is_atom(key) do
if key in Map.keys(struct),
do: {:ok, key},
else: {:raise, ArgumentError, "key #{key} not found in struct #{name}"}
end
defp find_key(struct, options) when is_list(options) do
case Keyword.fetch(options, :key) do
{:ok, key} ->
find_key(struct, key)
:error ->
{:raise, ArgumentError,
"""
A :key option is required when deriving Mogs.Timers:
@derive {Mogs.Timers.Store, key: :timers}
defstruct timers: Mogs.Timers.new(), other_key: …
"""}
end
end
end