Current section
Files
Jump to
Current section
Files
lib/ps2/ess/subscription.ex
defmodule PS2.ESS.Subscription do
@enforce_keys ~w|characters events worlds logical_and_characters_with_worlds|a
defstruct characters: :all,
clear?: false,
events: :all,
worlds: :all,
logical_and_characters_with_worlds: false
@opaque t() :: %__MODULE__{
characters: MapSet.t() | :all,
clear?: boolean(),
events: MapSet.t() | :all,
worlds: MapSet.t() | :all,
logical_and_characters_with_worlds: boolean()
}
@set_keys ~w|characters events worlds|a
def new(nil), do: nil
def new(%__MODULE__{} = sub), do: sub
def new(attrs), do: struct(__MODULE__, map_attrs(attrs))
defp map_attrs(attrs) do
Enum.map(attrs, fn
{k, v} when k in @set_keys and is_list(v) -> {k, MapSet.new(v)}
{k, v} -> {k, v}
end)
end
def merge(sub, sub), do: sub
def merge(%__MODULE__{} = sub, sub_or_attrs) when not is_nil(sub_or_attrs) do
%__MODULE__{} = sub2 = new(sub_or_attrs)
clear? = sub2.clear?
merged_set_attrs = Enum.map(@set_keys, &{&1, merge_sub_set(Map.fetch!(sub, &1), Map.fetch!(sub2, &1), clear?)})
attrs =
Keyword.merge(merged_set_attrs,
clear?: false,
logical_and_characters_with_worlds: sub2.logical_and_characters_with_worlds
)
struct!(sub, attrs)
end
defp merge_sub_set(maybe_all, maybe_all2, false) when :all in [maybe_all, maybe_all2], do: :all
# this case is not really doable - just keep it :all
defp merge_sub_set(:all, %MapSet{}, true), do: :all
defp merge_sub_set(maybe_all, maybe_all2, true) when :all in [maybe_all, maybe_all2], do: MapSet.new()
defp merge_sub_set(%MapSet{} = set1, %MapSet{} = set2, false), do: MapSet.union(set1, set2)
defp merge_sub_set(%MapSet{} = set1, %MapSet{} = set2, true), do: MapSet.difference(set1, set2)
def summary(nil), do: "no subscriptions"
def summary(%__MODULE__{} = sub) do
char_count = set_summary(sub.characters)
events_count = set_summary(sub.events)
worlds_count = set_summary(sub.worlds)
chars_and_worlds_op = if sub.logical_and_characters_with_worlds, do: "AND", else: "OR"
"#{events_count} events for #{char_count} chars #{chars_and_worlds_op} #{worlds_count} worlds"
end
defp set_summary(:all), do: "all"
defp set_summary(%MapSet{} = set), do: MapSet.size(set)
defimpl JSON.Encoder do
def encode(sub, opts) do
action = if sub.clear?, do: "clearSubscribe", else: "subscribe"
JSON.Encoder.Map.encode(
%{
"service" => "event",
"action" => action,
"characters" => encode_set(sub.characters),
"worlds" => encode_set(sub.worlds),
"eventNames" => encode_set(sub.events)
},
opts
)
end
defp encode_set(:all), do: ["all"]
defp encode_set(%MapSet{} = set), do: Enum.map(set, &to_string/1)
end
end