Current section

Files

Jump to
smppex lib smppex protocol mandatory_fields_builder.ex
Raw

lib/smppex/protocol/mandatory_fields_builder.ex

defmodule SMPPEX.Protocol.MandatoryFieldsBuilder do
@moduledoc false
alias SMPPEX.Protocol.Pack
alias SMPPEX.Protocol.MandatoryFieldsSpecs
@spec build(map, MandatoryFieldsSpecs.fields_spec()) :: {:ok, iodata} | {:error, any}
def build(fields, spec) when is_map(fields) do
build(fields, Enum.reverse(spec), [])
end
defp build(_fields, [], built), do: {:ok, built}
defp build(fields, [field_spec | specs], built) do
case build_field(fields, field_spec) do
{:ok, new_fields, bin} -> build(new_fields, specs, [bin | built])
{:error, error} -> {:error, error}
end
end
defp build_field(fields, {name, {:octet_string, len}}) when is_atom(len) do
case fields[name] do
bin when is_binary(bin) -> {:ok, Map.put(fields, len, byte_size(bin)), bin}
_ -> {:error, "Field #{name} is not an octet_string"}
end
end
defp build_field(fields, {name, {:times, times, subspecs}}) when is_atom(times) do
case fields[name] do
values when is_list(values) ->
case build_subfields(values, subspecs, []) do
{:ok, bins} -> {:ok, Map.put(fields, times, length(values)), bins}
{:error, error} -> {:error, error}
end
_ ->
{:error, "Field #{name} is not a list"}
end
end
defp build_field(fields, {:case, cases}) when is_list(cases) do
build_cases(fields, cases)
end
defp build_field(fields, {name, simple_spec}) when is_tuple(simple_spec) do
case build_simple_value(fields[name], simple_spec) do
{:ok, bin} -> {:ok, fields, bin}
{:error, error} -> {:error, {"Error building simple field #{name}", error}}
end
end
defp build_subfields([], _specs, built), do: {:ok, Enum.reverse(built)}
defp build_subfields([value | values], specs, built) do
case build(value, specs) do
{:ok, bin} -> build_subfields(values, specs, [bin | built])
{:error, error} -> {:error, error}
end
end
defp build_cases(_fields, []), do: {:error, "No case matched given fields"}
defp build_cases(fields, [{cond_name, cond_value, specs} | other_cases]) do
if fields[cond_name] == cond_value do
case build(fields, specs) do
{:ok, bin} -> {:ok, fields, bin}
{:error, error} -> {:error, error}
end
else
build_cases(fields, other_cases)
end
end
defp build_simple_value(value, {:c_octet_string, {:max, n}}),
do: Pack.c_octet_string(value, {:max, n})
defp build_simple_value(value, {:c_octet_string, {:fixed, n}}),
do: Pack.c_octet_string(value, {:fixed, n})
defp build_simple_value(value, {:octet_string, n}), do: Pack.octet_string(value, n)
defp build_simple_value(value, {:integer, n}), do: Pack.integer(value, n)
end