Current section

Files

Jump to
combo lib combo socket serializers v2_json_serializer.ex
Raw

lib/combo/socket/serializers/v2_json_serializer.ex

defmodule Combo.Socket.V2.JSONSerializer do
@moduledoc false
@behaviour Combo.Socket.Serializer
@push 0
@reply 1
@broadcast 2
alias Combo.Socket.{Broadcast, Message, Reply}
@impl true
def fastlane!(%Broadcast{payload: {:binary, data}} = msg) do
topic_size = byte_size!(msg.topic, :topic, 255)
event_size = byte_size!(msg.event, :event, 255)
bin = <<
@broadcast::size(8),
topic_size::size(8),
event_size::size(8),
msg.topic::binary-size(topic_size),
msg.event::binary-size(event_size),
data::binary
>>
{:socket_push, :binary, bin}
end
def fastlane!(%Broadcast{payload: %{}} = msg) do
data = Combo.json_library().encode_to_iodata!([nil, nil, msg.topic, msg.event, msg.payload])
{:socket_push, :text, data}
end
def fastlane!(%Broadcast{payload: invalid}) do
raise ArgumentError, "expected broadcasted payload to be a map, got: #{inspect(invalid)}"
end
@impl true
def encode!(%Reply{payload: {:binary, data}} = reply) do
status = to_string(reply.status)
join_ref = to_string(reply.join_ref)
ref = to_string(reply.ref)
join_ref_size = byte_size!(join_ref, :join_ref, 255)
ref_size = byte_size!(ref, :ref, 255)
topic_size = byte_size!(reply.topic, :topic, 255)
status_size = byte_size!(status, :status, 255)
bin = <<
@reply::size(8),
join_ref_size::size(8),
ref_size::size(8),
topic_size::size(8),
status_size::size(8),
join_ref::binary-size(join_ref_size),
ref::binary-size(ref_size),
reply.topic::binary-size(topic_size),
status::binary-size(status_size),
data::binary
>>
{:socket_push, :binary, bin}
end
def encode!(%Reply{} = reply) do
data = [
reply.join_ref,
reply.ref,
reply.topic,
"combo_reply",
%{status: reply.status, response: reply.payload}
]
{:socket_push, :text, Combo.json_library().encode_to_iodata!(data)}
end
def encode!(%Message{payload: {:binary, data}} = msg) do
join_ref = to_string(msg.join_ref)
join_ref_size = byte_size!(join_ref, :join_ref, 255)
topic_size = byte_size!(msg.topic, :topic, 255)
event_size = byte_size!(msg.event, :event, 255)
bin = <<
@push::size(8),
join_ref_size::size(8),
topic_size::size(8),
event_size::size(8),
join_ref::binary-size(join_ref_size),
msg.topic::binary-size(topic_size),
msg.event::binary-size(event_size),
data::binary
>>
{:socket_push, :binary, bin}
end
def encode!(%Message{payload: %{}} = msg) do
data = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]
{:socket_push, :text, Combo.json_library().encode_to_iodata!(data)}
end
def encode!(%Message{payload: invalid}) do
raise ArgumentError, "expected payload to be a map, got: #{inspect(invalid)}"
end
@impl true
def decode!(raw_message, opts) do
case Keyword.fetch(opts, :opcode) do
{:ok, :text} -> decode_text(raw_message)
{:ok, :binary} -> decode_binary(raw_message)
end
end
defp decode_text(raw_message) do
[join_ref, ref, topic, event, payload | _] = Combo.json_library().decode!(raw_message)
%Message{
topic: topic,
event: event,
payload: payload,
ref: ref,
join_ref: join_ref
}
end
defp decode_binary(<<
@push::size(8),
join_ref_size::size(8),
ref_size::size(8),
topic_size::size(8),
event_size::size(8),
join_ref::binary-size(join_ref_size),
ref::binary-size(ref_size),
topic::binary-size(topic_size),
event::binary-size(event_size),
data::binary
>>) do
%Message{
topic: topic,
event: event,
payload: {:binary, data},
ref: ref,
join_ref: join_ref
}
end
defp byte_size!(bin, kind, max) do
case byte_size(bin) do
size when size <= max ->
size
oversized ->
raise ArgumentError, """
unable to convert #{kind} to binary.
#{inspect(bin)}
must be less than or equal to #{max} bytes, but is #{oversized} bytes.
"""
end
end
end