lib/chainweb/pact/spv_request_body.ex

defmodule Kadena.Chainweb.Pact.SPVRequestBody do
  @moduledoc """
  `SPVRequestBody` struct definition.
  """

  alias Kadena.Types.{Base64Url, ChainID}

  @behaviour Kadena.Chainweb.Type

  @type request_key :: Base64Url.t()
  @type target_chain_id :: ChainID.t()
  @type value :: request_key() | target_chain_id()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}

  @type t :: %__MODULE__{request_key: request_key(), target_chain_id: target_chain_id()}

  defstruct [:request_key, :target_chain_id]

  @impl true
  def new(args) when is_list(args) do
    request_key = Keyword.get(args, :request_key)
    target_chain_id = Keyword.get(args, :target_chain_id)

    with {:ok, request_key} <- validate_request_key(request_key),
         {:ok, target_chain_id} <- validate_target_chain_id(target_chain_id) do
      %__MODULE__{request_key: request_key, target_chain_id: target_chain_id}
    end
  end

  def new(_args), do: {:error, [spv_request_body: :not_a_list]}

  @impl true
  def to_json!(%__MODULE__{
        request_key: %Base64Url{url: request_key},
        target_chain_id: %ChainID{id: target_chain_id}
      }) do
    Jason.encode!(%{requestKey: request_key, targetChainId: target_chain_id})
  end

  @spec validate_request_key(request_key :: request_key()) :: validation()
  defp validate_request_key(%Base64Url{} = request_key), do: {:ok, request_key}

  defp validate_request_key(request_key) do
    case Base64Url.new(request_key) do
      %Base64Url{} = request_key -> {:ok, request_key}
      {:error, _reasons} -> {:error, [request_key: :invalid]}
    end
  end

  @spec validate_target_chain_id(target_chain_id :: target_chain_id()) :: validation()
  defp validate_target_chain_id(%ChainID{} = target_chain_id), do: {:ok, target_chain_id}

  defp validate_target_chain_id(target_chain_id) do
    case ChainID.new(target_chain_id) do
      %ChainID{} = target_chain_id -> {:ok, target_chain_id}
      {:error, reasons} -> {:error, [target_chain_id: :invalid] ++ reasons}
    end
  end
end