lib/types/command.ex

defmodule Kadena.Types.Command do
  @moduledoc """
  `Command` struct definition.
  """

  alias Kadena.Types.{PactTransactionHash, Signature}

  @behaviour Kadena.Types.Spec

  @type hash :: PactTransactionHash.t()
  @type sigs :: list(Signature.t())
  @type cmd :: String.t()
  @type raw_cmd :: list()
  @type value :: hash() | sigs() | cmd()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}

  @type t :: %__MODULE__{hash: hash(), sigs: sigs(), cmd: cmd()}

  defstruct [:hash, :sigs, :cmd]

  @impl true
  def new(args) when is_list(args) do
    hash = Keyword.get(args, :hash)
    sigs = Keyword.get(args, :sigs)
    cmd = Keyword.get(args, :cmd)

    with {:ok, hash} <- validate_hash(hash),
         {:ok, sigs} <- validate_sigs(sigs),
         {:ok, cmd} <- validate_cmd(cmd) do
      %__MODULE__{hash: hash, sigs: sigs, cmd: cmd}
    end
  end

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

  @spec validate_hash(hash :: hash()) :: validation()
  defp validate_hash(%PactTransactionHash{} = hash), do: {:ok, hash}

  defp validate_hash(hash) do
    case PactTransactionHash.new(hash) do
      %PactTransactionHash{} = hash -> {:ok, hash}
      {:error, _reason} -> {:error, [hash: :invalid]}
    end
  end

  @spec validate_sigs(sigs :: sigs()) :: validation()
  defp validate_sigs([]), do: {:ok, []}
  defp validate_sigs([%Signature{} | _tail] = sigs), do: {:ok, sigs}
  defp validate_sigs(_hash), do: {:error, [sigs: :invalid]}

  @spec validate_cmd(cmd :: raw_cmd()) :: validation()
  defp validate_cmd(cmd) when is_binary(cmd), do: {:ok, cmd}
  defp validate_cmd(_cmd), do: {:error, [cmd: :not_a_string]}
end