Current section
Files
Jump to
Current section
Files
lib/xdr/union.ex
defmodule XDR.Union do
@behaviour XDR.Declaration
@moduledoc """
this module is in charge of process the Discriminated Union types based on the RFC4506 XDR Standard
"""
defstruct discriminant: nil, arms: nil
@type t :: %XDR.Union{discriminant: XDR.Enum | XDR.Int | XDR.UInt, arms: list}
alias XDR.Error.Union
def new(discriminant, arms), do: %XDR.Union{discriminant: discriminant, arms: arms}
@impl XDR.Declaration
@doc """
this function is in charge of encoding the Discriminated Union types, it receives a struct which contains the discriminant
which is the principal value to make the Union, and the arms which are the possible values that can be taken by the discriminant
returns an ok tuple which contains the binary encoded of the union
"""
@spec encode_xdr(map()) :: {:ok, binary()} | {:error, :not_atom}
def encode_xdr(%{discriminant: %{identifier: identifier}}) when not is_atom(identifier),
do: {:error, :not_atom}
def encode_xdr(%{
discriminant: %{identifier: identifier} = discriminant,
arms: arms
}) do
discriminant_module = discriminant.__struct__
encoded_discriminant = discriminant_module.encode_xdr!(discriminant)
arm = arms[identifier]
arm_module = arm.__struct__
encoded_arm = arm_module.encode_xdr!(arm)
{:ok, encoded_discriminant <> encoded_arm}
end
def encode_xdr(%{discriminant: discriminant, arms: arms}) do
discriminant_module = discriminant.__struct__
encoded_discriminant = discriminant_module.encode_xdr!(discriminant)
arm = arms[discriminant.datum]
arm_module = arm.__struct__
encoded_arm = arm_module.encode_xdr!(arm)
{:ok, encoded_discriminant <> encoded_arm}
end
@impl XDR.Declaration
@doc """
this function is in charge of encoding the Discriminated Union types, it receives a struct which contains the discriminant
which is the principal value to make the Union, and the arms which are the possible values that can be taken by the discriminant
returns the binary encoded of the union
"""
@spec encode_xdr!(map()) :: binary()
def encode_xdr!(union) do
case encode_xdr(union) do
{:ok, binary} -> binary
{:error, reason} -> raise(Union, reason)
end
end
@impl XDR.Declaration
@doc """
this function is in charge of decoding the XDR which represents an Disxriminated Union type, it receives a struct which contains the discriminant
which is the principal value to make the Union, and the arms which are the possible values that can be taken by the discriminant, and the specific
struct
returns an ok tuple which contains the values of the union
"""
@spec decode_xdr(bytes :: binary(), union :: map()) ::
{:ok, {any, binary()}} | {:error, :not_binary | :not_list}
def decode_xdr(bytes, union) do
decode_union_discriminant(bytes, union)
|> decode_union_arm()
end
@impl XDR.Declaration
@doc """
this function is in charge of decoding the XDR which represents an Disxriminated Union type, it receives a struct which contains the discriminant
which is the principal value to make the Union, and the arms which are the possible values that can be taken by the discriminant, and the specific
struct
returns the values of the union
"""
@spec decode_xdr!(bytes :: binary(), union :: map()) :: {any, binary()}
def decode_xdr!(bytes, union) do
case decode_xdr(bytes, union) do
{:ok, result} -> result
{:error, reason} -> raise(Union, reason)
end
end
@spec decode_union_discriminant(bytes :: binary, struct :: map()) ::
{struct(), binary} | {:error, :not_binary | :not_list}
defp decode_union_discriminant(bytes, %{discriminant: %{declarations: _declarations}})
when not is_binary(bytes),
do: {:error, :not_binary}
defp decode_union_discriminant(_bytes, %{discriminant: %{declarations: declarations}})
when not is_list(declarations),
do: {:error, :not_list}
defp decode_union_discriminant(
bytes,
%{discriminant: %{declarations: declarations}} = union
) do
{%{identifier: identifier}, rest} = XDR.Enum.decode_xdr!(bytes, %{declarations: declarations})
{%{union | discriminant: identifier}, rest}
end
@spec decode_union_discriminant(bytes :: binary, union :: map()) :: {struct(), binary}
defp decode_union_discriminant(bytes, _union) when not is_binary(bytes),
do: {:error, :not_binary}
defp decode_union_discriminant(bytes, %{discriminant: discriminant} = union) do
discriminant_module = discriminant.__struct__
{%{datum: datum}, rest} = discriminant_module.decode_xdr!(bytes)
{%{union | discriminant: datum}, rest}
end
@spec decode_union_arm({:error, atom}) :: {:error, atom}
defp decode_union_arm({:error, reason}), do: {:error, reason}
@spec decode_union_arm({map(), binary}) :: {:ok, {{atom | integer, any}, binary}}
defp decode_union_arm({%{discriminant: discriminant, arms: arms}, rest}) do
arm = arms[discriminant]
arm_module = arm.__struct__
{decoded_arm, rest} = arm_module.decode_xdr!(rest)
{:ok, {{discriminant, decoded_arm}, rest}}
end
end