lib/tx_build/ledger_key.ex

defmodule Stellar.TxBuild.LedgerKey do
  @moduledoc """
  `LedgerKey` struct definition.
  """

  alias StellarBase.XDR.{LedgerEntryType, LedgerKey}

  alias Stellar.TxBuild.Ledger.{
    Account,
    ClaimableBalance,
    Data,
    ConfigSetting,
    ContractCode,
    ContractData,
    TTL,
    LiquidityPool,
    Offer,
    Trustline
  }

  @behaviour Stellar.TxBuild.XDR

  @type type ::
          :account
          | :trustline
          | :offer
          | :data
          | :claimable_balance
          | :liquidity_pool
          | :contract_data
          | :contract_code
          | :config_setting
          | :ttl

  @type entry ::
          Account.t()
          | ClaimableBalance.t()
          | Data.t()
          | LiquidityPool.t()
          | Offer.t()
          | Trustline.t()
          | ContractData.t()
          | ContractCode.t()
          | ConfigSetting.t()
          | TTL.t()

  @type t :: %__MODULE__{type: type(), entry: entry()}

  defstruct [:type, :entry]

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

  def new({:account, [account_id: account_id]}, _opts) do
    case Account.new(account_id) do
      %Account{} = account -> %__MODULE__{type: :account, entry: account}
      _error -> {:error, :invalid_account}
    end
  end

  def new({:claimable_balance, [claimable_balance_id: claimable_balance_id]}, _opts) do
    case ClaimableBalance.new(claimable_balance_id) do
      %ClaimableBalance{} = claimable_balance ->
        %__MODULE__{type: :claimable_balance, entry: claimable_balance}

      _error ->
        {:error, :invalid_claimable_balance}
    end
  end

  def new({:liquidity_pool, [liquidity_pool_id: liquidity_pool_id]}, _opts) do
    case LiquidityPool.new(liquidity_pool_id) do
      %LiquidityPool{} = liquidity_pool ->
        %__MODULE__{type: :liquidity_pool, entry: liquidity_pool}

      _error ->
        {:error, :invalid_liquidity_pool}
    end
  end

  def new({:trustline, trustline}, _opts) do
    case Trustline.new(trustline) do
      %Trustline{} = trustline ->
        %__MODULE__{type: :trustline, entry: trustline}

      _error ->
        {:error, :invalid_trustline}
    end
  end

  def new({:offer, offer}, _opts) do
    case Offer.new(offer) do
      %Offer{} = offer -> %__MODULE__{type: :offer, entry: offer}
      _error -> {:error, :invalid_offer}
    end
  end

  def new({:data, data}, _opts) do
    case Data.new(data) do
      %Data{} = data -> %__MODULE__{type: :data, entry: data}
      _error -> {:error, :invalid_data}
    end
  end

  def new(
        {:contract_data, args},
        _opts
      ) do
    case ContractData.new(args) do
      %ContractData{} = contract_data ->
        %__MODULE__{type: :contract_data, entry: contract_data}

      _error ->
        {:error, :invalid_contract_data}
    end
  end

  def new(
        {:contract_code, args},
        _opts
      ) do
    case ContractCode.new(args) do
      %ContractCode{} = contract_code ->
        %__MODULE__{type: :contract_code, entry: contract_code}

      _error ->
        {:error, :invalid_contract_code}
    end
  end

  def new(
        {:config_setting, args},
        _opts
      ) do
    case ConfigSetting.new(args) do
      %ConfigSetting{} = config_setting ->
        %__MODULE__{type: :config_setting, entry: config_setting}

      _error ->
        {:error, :invalid_config_setting}
    end
  end

  def new(
        {:ttl, args},
        _opts
      ) do
    case TTL.new(args) do
      %TTL{} = ttl ->
        %__MODULE__{type: :ttl, entry: ttl}

      _error ->
        {:error, :invalid_ttl}
    end
  end

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

  @impl true
  def to_xdr(%__MODULE__{type: type, entry: %{__struct__: entry_type} = entry}) do
    ledger_entry_type = ledger_entry_type(type)

    entry
    |> entry_type.to_xdr()
    |> LedgerKey.new(ledger_entry_type)
  end

  @spec ledger_entry_type(type :: type()) :: LedgerEntryType.t()
  defp ledger_entry_type(:account), do: LedgerEntryType.new(:ACCOUNT)
  defp ledger_entry_type(:trustline), do: LedgerEntryType.new(:TRUSTLINE)
  defp ledger_entry_type(:offer), do: LedgerEntryType.new(:OFFER)
  defp ledger_entry_type(:data), do: LedgerEntryType.new(:DATA)
  defp ledger_entry_type(:claimable_balance), do: LedgerEntryType.new(:CLAIMABLE_BALANCE)
  defp ledger_entry_type(:liquidity_pool), do: LedgerEntryType.new(:LIQUIDITY_POOL)
  defp ledger_entry_type(:contract_data), do: LedgerEntryType.new(:CONTRACT_DATA)
  defp ledger_entry_type(:contract_code), do: LedgerEntryType.new(:CONTRACT_CODE)
  defp ledger_entry_type(:config_setting), do: LedgerEntryType.new(:CONFIG_SETTING)
  defp ledger_entry_type(:ttl), do: LedgerEntryType.new(:TTL)
end