lib/tx_build/contract_id_preimage.ex

defmodule Stellar.TxBuild.ContractIDPreimage do
  @moduledoc """
  `ContractIDPreimage` struct definition.
  """
  alias StellarBase.XDR.ContractIDPreimage
  alias StellarBase.XDR.ContractIDPreimageType
  alias Stellar.TxBuild.{Asset, ContractIDPreimageFromAddress}

  @behaviour Stellar.TxBuild.XDR

  @type type :: :from_address | :from_asset
  @type value :: Asset.t() | ContractIDPreimageFromAddress.t()
  @type error :: {:error, atom()}
  @type validation :: {:ok, any()} | error()
  @type t :: %__MODULE__{
          type: type(),
          value: value()
        }

  defstruct [:type, :value]

  @allowed_types ~w(from_address from_asset)a

  @impl true
  def new(value, opts \\ [])

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

  def new(_value, _opts), do: {:error, :invalid_contract_id_preimage}

  @impl true
  def to_xdr(%__MODULE__{type: :from_address, value: value}) do
    type = ContractIDPreimageType.new()

    value
    |> ContractIDPreimageFromAddress.to_xdr()
    |> ContractIDPreimage.new(type)
  end

  def to_xdr(%__MODULE__{type: :from_asset, value: value}) do
    type = ContractIDPreimageType.new(:CONTRACT_ID_PREIMAGE_FROM_ASSET)

    value
    |> Asset.to_xdr()
    |> ContractIDPreimage.new(type)
  end

  def to_xdr(_struct), do: {:error, :invalid_struct}

  @spec validate_contract_id_preimage({type :: atom(), value :: value()}) :: validation()
  defp validate_contract_id_preimage({:from_asset, %Asset{} = value}), do: {:ok, value}

  defp validate_contract_id_preimage({:from_address, %ContractIDPreimageFromAddress{} = value}),
    do: {:ok, value}

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