lib/tx_build/ledger/data.ex

defmodule Stellar.TxBuild.Ledger.Data do
  @moduledoc """
  Ledger `Data` struct definition.
  """
  alias StellarBase.XDR.{Data, String64}
  alias Stellar.TxBuild.AccountID

  @behaviour Stellar.TxBuild.XDR

  @type account_id :: String.t()
  @type data_name :: String.t()
  @type validation :: {:ok, any()} | {:error, atom()}

  @type t :: %__MODULE__{account_id: AccountID.t(), data_name: data_name()}

  defstruct [:account_id, :data_name]

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

  def new(args, _opts) do
    account_id = Keyword.get(args, :account_id)
    data_name = Keyword.get(args, :data_name)

    with {:ok, account_id} <- validate_account_id(account_id),
         {:ok, data_name} <- validate_data_name(data_name) do
      %__MODULE__{account_id: account_id, data_name: data_name}
    end
  end

  @impl true
  def to_xdr(%__MODULE__{account_id: account_id, data_name: data_name}) do
    data_name = String64.new(data_name)

    account_id
    |> AccountID.to_xdr()
    |> Data.new(data_name)
  end

  @spec validate_account_id(account_id :: account_id()) :: validation()
  defp validate_account_id(account_id) do
    case AccountID.new(account_id) do
      %AccountID{} = account_id -> {:ok, account_id}
      _error -> {:error, :invalid_account}
    end
  end

  @spec validate_data_name(data_name :: data_name()) :: validation()
  defp validate_data_name(data_name) when byte_size(data_name) <= 64, do: {:ok, data_name}
  defp validate_data_name(_data_name), do: {:error, :invalid_data_name}
end