lib/tx_build/hash_id_preimage.ex

defmodule Stellar.TxBuild.HashIDPreimage do
  @moduledoc """
  `HashIDPreimage` struct definition.
  """
  alias StellarBase.XDR.{EnvelopeType, HashIDPreimage}

  alias Stellar.TxBuild.{
    HashIDPreimageContractID,
    HashIDPreimageSorobanAuthorization,
    HashIDPreimageOperationID,
    HashIDPreimageRevokeID
  }

  @behaviour Stellar.TxBuild.XDR

  @type hash_id ::
          HashIDPreimageContractID.t()
          | HashIDPreimageOperationID.t()
          | HashIDPreimageRevokeID.t()
          | HashIDPreimageSorobanAuthorization.t()
  @type validation :: {:ok, any()} | {:error, atom()}
  @type type ::
          :op_id
          | :pool_revoke_op_id
          | :contract_id
          | :soroban_auth
  @type t :: %__MODULE__{type: type(), value: hash_id()}

  defstruct [:type, :value]

  @allowed_types ~w(op_id pool_revoke_op_id contract_id soroban_auth)a

  @impl true
  def new(args, opts \\ nil)

  def new([{type, value}], _opts) when type in @allowed_types do
    with {:ok, value} <- validate_hash_id_preimage({type, value}) do
      %__MODULE__{
        type: type,
        value: value
      }
    end
  end

  def new(_args, _opts), do: {:error, :invalid_hash_id_preimage_type}

  @impl true
  def to_xdr(%__MODULE__{
        type: :op_id,
        value: %HashIDPreimageOperationID{} = value
      }) do
    envelope_type = EnvelopeType.new(:ENVELOPE_TYPE_OP_ID)

    value
    |> HashIDPreimageOperationID.to_xdr()
    |> HashIDPreimage.new(envelope_type)
  end

  def to_xdr(%__MODULE__{
        type: :pool_revoke_op_id,
        value: %HashIDPreimageRevokeID{} = value
      }) do
    envelope_type = EnvelopeType.new(:ENVELOPE_TYPE_POOL_REVOKE_OP_ID)

    value
    |> HashIDPreimageRevokeID.to_xdr()
    |> HashIDPreimage.new(envelope_type)
  end

  def to_xdr(%__MODULE__{
        type: :contract_id,
        value: %HashIDPreimageContractID{} = value
      }) do
    envelope_type = EnvelopeType.new(:ENVELOPE_TYPE_CONTRACT_ID)

    value
    |> HashIDPreimageContractID.to_xdr()
    |> HashIDPreimage.new(envelope_type)
  end

  def to_xdr(%__MODULE__{
        type: :soroban_auth,
        value: %HashIDPreimageSorobanAuthorization{} = value
      }) do
    envelope_type = EnvelopeType.new(:ENVELOPE_TYPE_SOROBAN_AUTHORIZATION)

    value
    |> HashIDPreimageSorobanAuthorization.to_xdr()
    |> HashIDPreimage.new(envelope_type)
  end

  @spec validate_hash_id_preimage({atom(), hash_id()}) :: validation()
  defp validate_hash_id_preimage({:op_id, %HashIDPreimageOperationID{} = value}), do: {:ok, value}

  defp validate_hash_id_preimage({:pool_revoke_op_id, %HashIDPreimageRevokeID{} = value}),
    do: {:ok, value}

  defp validate_hash_id_preimage({:contract_id, %HashIDPreimageContractID{} = value}),
    do: {:ok, value}

  defp validate_hash_id_preimage({:soroban_auth, %HashIDPreimageSorobanAuthorization{} = value}),
    do: {:ok, value}

  defp validate_hash_id_preimage({type, _value}), do: {:error, :"invalid_#{type}"}
end