lib/tx_build/ledger/contract_data.ex

defmodule Stellar.TxBuild.Ledger.ContractData do
  @moduledoc """
  `ContractData` struct definition.
  """

  import Stellar.TxBuild.Validations,
    only: [validate_address: 1]

  alias Stellar.TxBuild.{SCAddress, SCVal}
  alias StellarBase.XDR.{ContractDataDurability, LedgerKeyContractData}

  @behaviour Stellar.TxBuild.XDR

  @type error :: {:error, atom()}
  @type validation :: {:ok, any()} | error()
  @type contract :: SCAddress.t()
  @type key :: SCVal.t()
  @type durability :: :temporary | :persistent

  @type t :: %__MODULE__{
          contract: contract(),
          key: key(),
          durability: durability()
        }

  defstruct [:contract, :key, :durability]

  @allowed_durabilities ~w(temporary persistent)a

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

  def new(
        [
          {:contract, contract},
          {:key, key},
          {:durability, durability}
        ],
        _opts
      )
      when durability in @allowed_durabilities do
    with {:ok, contract} <- validate_address(contract),
         {:ok, key} <- validate_sc_val_ledger_instance(key) do
      %__MODULE__{
        contract: contract,
        key: key,
        durability: durability
      }
    end
  end

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

  @impl true
  def to_xdr(%__MODULE__{
        contract: contract,
        key: key,
        durability: durability
      }) do
    key = SCVal.to_xdr(key)
    durability = durability_to_xdr(durability)

    contract
    |> SCAddress.to_xdr()
    |> LedgerKeyContractData.new(key, durability)
  end

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

  @spec validate_sc_val_ledger_instance(key :: key()) :: validation()
  defp validate_sc_val_ledger_instance(%SCVal{} = key), do: {:ok, key}
  defp validate_sc_val_ledger_instance(_key), do: {:error, :invalid_key}

  @spec durability_to_xdr(atom()) :: ContractDataDurability.t()
  defp durability_to_xdr(:temporary), do: ContractDataDurability.new(:TEMPORARY)
  defp durability_to_xdr(:persistent), do: ContractDataDurability.new(:PERSISTENT)
end