lib/xdr/contract/spec/sc_spec_entry.ex

defmodule StellarBase.XDR.SCSpecEntry do
  @moduledoc """
  Representation of Stellar `SCSpecEntry` type.
  """

  alias StellarBase.XDR.{
    SCSpecEntryKind,
    SCSpecFunctionV0,
    SCSpecUDTStructV0,
    SCSpecUDTUnionV0,
    SCSpecUDTEnumV0,
    SCSpecUDTErrorEnumV0
  }

  @behaviour XDR.Declaration

  @arms [
    SC_SPEC_ENTRY_FUNCTION_V0: SCSpecFunctionV0,
    SC_SPEC_ENTRY_UDT_STRUCT_V0: SCSpecUDTStructV0,
    SC_SPEC_ENTRY_UDT_UNION_V0: SCSpecUDTUnionV0,
    SC_SPEC_ENTRY_UDT_ENUM_V0: SCSpecUDTEnumV0,
    SC_SPEC_ENTRY_UDT_ERROR_ENUM_V0: SCSpecUDTErrorEnumV0
  ]

  @type code ::
          SCSpecFunctionV0.t()
          | SCSpecUDTStructV0.t()
          | SCSpecUDTUnionV0.t()
          | SCSpecUDTEnumV0.t()
          | SCSpecUDTErrorEnumV0.t()

  @type kind :: SCSpecEntryKind.t()

  @type t :: %__MODULE__{code: code(), kind: kind()}

  defstruct [:code, :kind]

  @spec new(code :: code(), kind :: kind()) :: t()
  def new(code, %SCSpecEntryKind{} = kind), do: %__MODULE__{code: code, kind: kind}

  @impl true
  def encode_xdr(%__MODULE__{code: code, kind: kind}) do
    kind
    |> XDR.Union.new(@arms, code)
    |> XDR.Union.encode_xdr()
  end

  @impl true
  def encode_xdr!(%__MODULE__{code: code, kind: kind}) do
    kind
    |> XDR.Union.new(@arms, code)
    |> XDR.Union.encode_xdr!()
  end

  @impl true
  def decode_xdr(bytes, spec \\ union_spec())

  def decode_xdr(bytes, spec) do
    case XDR.Union.decode_xdr(bytes, spec) do
      {:ok, {{kind, code}, rest}} -> {:ok, {new(code, kind), rest}}
      error -> error
    end
  end

  @impl true
  def decode_xdr!(bytes, spec \\ union_spec())

  def decode_xdr!(bytes, spec) do
    {{kind, code}, rest} = XDR.Union.decode_xdr!(bytes, spec)
    {new(code, kind), rest}
  end

  @spec union_spec() :: XDR.Union.t()
  defp union_spec do
    nil
    |> SCSpecEntryKind.new()
    |> XDR.Union.new(@arms)
  end
end