lib/types/command_payload.ex

defmodule Kadena.Types.CommandPayload do
  @moduledoc """
  `CommandPayload` struct definition.
  """
  alias Kadena.Types.{MetaData, NetworkID, Nonce, PactPayload, SignersList}

  @behaviour Kadena.Types.Spec

  @type network_id :: NetworkID.t() | nil
  @type payload :: PactPayload.t()
  @type signers :: SignersList.t()
  @type meta :: MetaData.t()
  @type nonce :: String.t()
  @type value :: network_id() | payload() | signers() | meta() | nonce()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}
  @type t :: %__MODULE__{
          network_id: network_id(),
          payload: payload(),
          signers: signers(),
          meta: meta(),
          nonce: nonce()
        }

  defstruct [:network_id, :payload, :signers, :meta, :nonce]

  @impl true
  def new(args) do
    network_id = Keyword.get(args, :network_id)
    payload = Keyword.get(args, :payload)
    signers = Keyword.get(args, :signers, [])
    meta = Keyword.get(args, :meta)
    nonce = Keyword.get(args, :nonce)

    with {:ok, network_id} <- validate_network_id(network_id),
         {:ok, payload} <- validate_payload(payload),
         {:ok, signers} <- validate_signers(signers),
         {:ok, meta} <- validate_meta(meta),
         {:ok, nonce} <- validate_nonce(nonce) do
      %__MODULE__{
        network_id: network_id,
        payload: payload,
        signers: signers,
        meta: meta,
        nonce: nonce
      }
    end
  end

  @spec validate_network_id(network_id :: atom()) :: validation()
  defp validate_network_id(network_id) do
    case NetworkID.new(network_id) do
      %NetworkID{} = network_id -> {:ok, network_id}
      _error -> {:error, [:network_id, :invalid]}
    end
  end

  @spec validate_payload(payload :: map()) :: validation()
  defp validate_payload(payload) do
    case PactPayload.new(payload) do
      %PactPayload{} = payload -> {:ok, payload}
      _error -> {:error, [:payload, :invalid]}
    end
  end

  @spec validate_signers(signers :: list()) :: validation()
  defp validate_signers(%SignersList{} = signers), do: {:ok, signers}

  defp validate_signers(signers) do
    case SignersList.new(signers) do
      %SignersList{} = signers -> {:ok, signers}
      _error -> {:error, [:signers, :invalid]}
    end
  end

  @spec validate_meta(meta :: meta()) :: validation()
  defp validate_meta(%MetaData{} = meta), do: {:ok, meta}
  defp validate_meta(_meta), do: {:error, [:meta, :invalid]}

  @spec validate_nonce(nonce :: nonce()) :: validation()
  defp validate_nonce(nonce) do
    case Nonce.new(nonce) do
      %Nonce{} -> {:ok, nonce}
      {:error, _reason} -> {:error, [:nonce, :invalid]}
    end
  end
end