lib/types/signed_command.ex

defmodule Kadena.Types.SignedCommand do
  @moduledoc """
  `SignedCommand` struct definition.
  """
  alias Kadena.Types.Signature

  @behaviour Kadena.Types.Spec

  @type str :: String.t()
  @type hash :: str()
  @type cmd :: str()
  @type sigs :: list(Signature.t())
  @type value :: str() | sigs()
  @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) do
    hash = Keyword.get(args, :hash)
    sigs = Keyword.get(args, :sigs)
    cmd = Keyword.get(args, :cmd)

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

  @spec validate_str(field :: atom(), value :: str()) :: validation()
  defp validate_str(_field, value) when is_binary(value), do: {:ok, value}
  defp validate_str(field, _value), do: {:error, [{field, :invalid}]}

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