Current section

Files

Jump to
smppex lib smppex protocol optional_fields_parser.ex
Raw

lib/smppex/protocol/optional_fields_parser.ex

defmodule SMPPEX.Protocol.OptionalFieldsParser do
@moduledoc false
alias SMPPEX.Protocol.Unpack
alias SMPPEX.Protocol.TlvFormat
@type parse_result :: {:ok, map} | {:error, reason :: term}
@spec parse(binary) :: parse_result
def parse(bin), do: parse(bin, Map.new())
defp parse(<<>>, parsed_fields) do
{:ok, parsed_fields}
end
defp parse(bin, parsed_fields) do
case Unpack.tlv(bin) do
{:ok, {tag, value}, rest} ->
case parse_format(tag, value) do
{:ok, parsed} ->
parse(rest, Map.put(parsed_fields, tag, parsed))
{:error, error} ->
{:error, {"Invalid format for tlv #{inspect(tag)}", error}}
end
{:error, _} = err ->
err
end
end
defp parse_format(tag, value) do
case TlvFormat.format_by_id(tag) do
{:ok, format} ->
parse_known_tlv(value, format)
# unknown tlvs are always valid
:unknown ->
{:ok, value}
end
end
defp parse_known_tlv(value, {:integer, size}) do
bit_length = size * 8
case value do
<<int::big-unsigned-integer-size(bit_length)>> -> {:ok, int}
_ -> {:error, "Invalid integer"}
end
end
defp parse_known_tlv(value, {:c_octet_string, {:max, size}}) do
case Unpack.c_octet_string(value, {:max, size}) do
{:ok, str, ""} -> {:ok, str}
_ -> {:error, "Invalid c_octet_string"}
end
end
defp parse_known_tlv(value, {:octet_string, size}) when is_integer(size) do
case value do
<<_::binary-size(size)>> -> {:ok, value}
_ -> {:error, "Invalid octet_string"}
end
end
defp parse_known_tlv(value, {:octet_string, {from, to}}) do
if byte_size(value) >= from and byte_size(value) <= to do
{:ok, value}
else
{:error, "Invalid octet_string"}
end
end
end