lib/types/provenance.ex

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

  alias Kadena.Types.ChainID

  @behaviour Kadena.Types.Spec

  @type chain_id :: ChainID.t()
  @type target_chain_id :: String.t()
  @type module_hash :: String.t()
  @type value :: chain_id() | module_hash()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}

  @type t :: %__MODULE__{
          target_chain_id: chain_id(),
          module_hash: module_hash()
        }

  defstruct [:target_chain_id, :module_hash]

  @impl true
  def new(args) do
    target_chain_id = Keyword.get(args, :target_chain_id)
    module_hash = Keyword.get(args, :module_hash)

    with {:ok, chain_id} <- validate_target_chain_id(target_chain_id),
         {:ok, hash} <- validate_module_hash(module_hash) do
      %__MODULE__{target_chain_id: chain_id, module_hash: hash}
    end
  end

  @spec validate_target_chain_id(target_chain_id :: target_chain_id()) :: validation()
  defp validate_target_chain_id(target_chain_id) do
    case ChainID.new(target_chain_id) do
      %ChainID{} = chain_id -> {:ok, chain_id}
      {:error, _reason} -> {:error, [target_chain_id: :invalid]}
    end
  end

  @spec validate_module_hash(module_hash :: module_hash()) :: validation()
  defp validate_module_hash(module_hash) when is_binary(module_hash), do: {:ok, module_hash}
  defp validate_module_hash(_module_hash), do: {:error, [module_hash: :invalid]}
end