lib/types/struct.ex

defmodule Soroban.Types.Struct do
  @moduledoc """
  `Struct` struct definition.
  """

  @behaviour Soroban.Types.Spec

  alias Soroban.Types.StructField
  alias Stellar.TxBuild.SCVal

  defstruct [:values]

  @type errors :: atom()
  @type values :: list(StructField.t())
  @type validation :: {:ok, values()} | {:error, errors()}
  @type t :: %__MODULE__{values: values}

  @impl true
  def new(values) when is_list(values) do
    with {:ok, values} <- validate_struct_field_values(values) do
      %__MODULE__{values: values}
    end
  end

  def new(_values), do: {:error, :invalid}

  @impl true
  def to_sc_val(%__MODULE__{values: values}) do
    values
    |> Enum.map(&StructField.to_sc_map_entry/1)
    |> (&SCVal.new(map: &1)).()
  end

  def to_sc_val(_error), do: {:error, :invalid_struct}

  @spec validate_struct_field_values(values :: values()) :: validation()
  def validate_struct_field_values(values) do
    if Enum.all?(values, &is_struct(&1, StructField)),
      do: {:ok, values},
      else: {:error, :invalid_values}
  end
end