lib/types/pact_event.ex

defmodule Kadena.Types.PactEvent do
  @moduledoc """
  `PactEventModule` struct definition.
  """

  alias Kadena.Types.{PactEventModule, PactValuesList}

  @behaviour Kadena.Types.Spec

  @type name :: String.t()
  @type pact_event_module :: PactEventModule.t()
  @type pact_event_module_arg :: pact_event_module() | list()
  @type params :: PactValuesList.t()
  @type params_arg :: params() | list()
  @type module_hash :: String.t()
  @type str :: String.t()
  @type value :: name() | pact_event_module_arg() | params_arg() | module_hash()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}

  @type t :: %__MODULE__{
          name: name(),
          module: pact_event_module(),
          params: params(),
          module_hash: module_hash()
        }

  defstruct [:name, :module, :params, :module_hash]

  @impl true
  def new(args) do
    name = Keyword.get(args, :name)
    pact_event_module = Keyword.get(args, :module)
    params = Keyword.get(args, :params)
    module_hash = Keyword.get(args, :module_hash)

    with {:ok, name} <- validate_string(:name, name),
         {:ok, pact_event_module} <- validate_pact_event_module(pact_event_module),
         {:ok, params} <- validate_params(params),
         {:ok, module_hash} <- validate_string(:module_hash, module_hash) do
      %__MODULE__{name: name, module: pact_event_module, params: params, module_hash: module_hash}
    end
  end

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

  @spec validate_pact_event_module(pact_event_module :: pact_event_module_arg()) :: validation()
  defp validate_pact_event_module(%PactEventModule{} = pact_event_module),
    do: {:ok, pact_event_module}

  defp validate_pact_event_module(pact_event_module) when is_list(pact_event_module) do
    case PactEventModule.new(pact_event_module) do
      %PactEventModule{} = event_module -> {:ok, event_module}
      {:error, reason} -> {:error, [module: :invalid] ++ reason}
    end
  end

  defp validate_pact_event_module(_pact_event_module), do: {:error, [module: :invalid]}

  @spec validate_params(params :: params_arg()) :: validation()
  defp validate_params(%PactValuesList{} = pact_values), do: {:ok, pact_values}

  defp validate_params(params) when is_list(params) do
    case PactValuesList.new(params) do
      %PactValuesList{} = pact_values -> {:ok, pact_values}
      {:error, reason} -> {:error, [params: :invalid] ++ reason}
    end
  end

  defp validate_params(_params), do: {:error, [params: :invalid]}
end