lib/types/signing_cap.ex

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

  alias Kadena.Types.Cap

  @behaviour Kadena.Types.Spec

  @type str :: String.t()
  @type role :: str()
  @type description :: str()
  @type cap :: Cap.t()
  @type value :: str() | cap()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}

  @type t :: %__MODULE__{role: role(), description: description(), cap: cap()}

  defstruct [:role, :description, :cap]

  @impl true
  def new(args) when is_list(args) do
    role = Keyword.get(args, :role)
    description = Keyword.get(args, :description)
    cap = Keyword.get(args, :cap)

    with {:ok, role} <- validate_str(:role, role),
         {:ok, description} <- validate_str(:description, description),
         {:ok, cap} <- validate_cap(cap) do
      %__MODULE__{role: role, description: description, cap: cap}
    end
  end

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

  @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_cap(cap :: cap()) :: validation()
  defp validate_cap(%Cap{} = cap), do: {:ok, cap}

  defp validate_cap(cap) do
    case Cap.new(cap) do
      %Cap{} = cap -> {:ok, cap}
      {:error, reasons} -> {:error, [cap: :invalid] ++ reasons}
    end
  end
end