lib/tx_build/soroban_resources.ex

defmodule Stellar.TxBuild.SorobanResources do
  @moduledoc """
  `SorobanResources` struct definition.
  """
  alias StellarBase.XDR.{SorobanResources, UInt32}
  alias Stellar.TxBuild.LedgerFootprint

  @behaviour Stellar.TxBuild.XDR

  @type error :: {:error, atom()}
  @type validation :: {:ok, any()} | error()
  @type footprint :: LedgerFootprint.t()
  @type instructions :: integer()
  @type read_bytes :: integer()
  @type write_bytes :: integer()
  @type extended_meta_data_size_bytes :: integer()

  @type t :: %__MODULE__{
          footprint: footprint(),
          instructions: instructions(),
          read_bytes: read_bytes(),
          write_bytes: write_bytes(),
          extended_meta_data_size_bytes: extended_meta_data_size_bytes()
        }

  defstruct [
    :footprint,
    :instructions,
    :read_bytes,
    :write_bytes,
    :extended_meta_data_size_bytes
  ]

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

  def new(
        [
          {:footprint, footprint},
          {:instructions, instructions},
          {:read_bytes, read_bytes},
          {:write_bytes, write_bytes},
          {:extended_meta_data_size_bytes, extended_meta_data_size_bytes}
        ],
        _opts
      )
      when is_integer(instructions) and is_integer(read_bytes) and is_integer(write_bytes) and
             is_integer(extended_meta_data_size_bytes) do
    with {:ok, footprint} <- validate_footprint(footprint) do
      %__MODULE__{
        footprint: footprint,
        instructions: instructions,
        read_bytes: read_bytes,
        write_bytes: write_bytes,
        extended_meta_data_size_bytes: extended_meta_data_size_bytes
      }
    end
  end

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

  @impl true
  def to_xdr(%__MODULE__{
        footprint: footprint,
        instructions: instructions,
        read_bytes: read_bytes,
        write_bytes: write_bytes,
        extended_meta_data_size_bytes: extended_meta_data_size_bytes
      }) do
    instructions = UInt32.new(instructions)
    read_bytes = UInt32.new(read_bytes)
    write_bytes = UInt32.new(write_bytes)
    extended_meta_data_size_bytes = UInt32.new(extended_meta_data_size_bytes)

    footprint
    |> LedgerFootprint.to_xdr()
    |> SorobanResources.new(
      instructions,
      read_bytes,
      write_bytes,
      extended_meta_data_size_bytes
    )
  end

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

  @spec validate_footprint(footprint :: footprint()) :: validation()
  defp validate_footprint(%LedgerFootprint{} = footprint), do: {:ok, footprint}
  defp validate_footprint(_footprint), do: {:error, :invalid_footprint}
end