lib/tx_build/ledger_footprint.ex

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

  alias Stellar.TxBuild.LedgerKey
  alias StellarBase.XDR.{LedgerFootprint, LedgerKeyList}

  @behaviour Stellar.TxBuild.XDR

  @type error :: {:error, atom()}
  @type validation :: {:ok, any()} | error()
  @type ledger_key :: LedgerKey.t()
  @type ledger_keys :: list(ledger_key)

  @type t :: %__MODULE__{
          read_only: ledger_keys(),
          read_write: ledger_keys()
        }

  defstruct [:read_only, :read_write]

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

  def new(args, _opts) when is_list(args) do
    read_only = Keyword.get(args, :read_only, [])
    read_write = Keyword.get(args, :read_write, [])

    with {:ok, read_only} <- validate_ledger_keys(read_only),
         {:ok, read_write} <- validate_ledger_keys(read_write) do
      %__MODULE__{
        read_only: read_only,
        read_write: read_write
      }
    end
  end

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

  @impl true
  def to_xdr(%__MODULE__{read_only: read_only, read_write: read_write}) do
    read_write = to_ledger_key_list(read_write)

    read_only
    |> to_ledger_key_list()
    |> LedgerFootprint.new(read_write)
  end

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

  @spec validate_ledger_keys(keys :: ledger_keys()) :: validation()
  defp validate_ledger_keys(keys) when is_list(keys) do
    if Enum.all?(keys, &validate_ledger_key/1),
      do: {:ok, keys},
      else: {:error, :invalid_ledger_keys}
  end

  defp validate_ledger_keys(_keys), do: {:error, :invalid_ledger_keys}

  @spec validate_ledger_key(ledger_key()) :: boolean()
  defp validate_ledger_key(%LedgerKey{}), do: true
  defp validate_ledger_key(_key), do: false

  @spec to_ledger_key_list(keys :: ledger_keys()) :: LedgerKeyList.t()
  defp to_ledger_key_list(keys) do
    keys
    |> Enum.map(&LedgerKey.to_xdr/1)
    |> LedgerKeyList.new()
  end
end