lib/types/yield.ex

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

  alias Kadena.Types.Provenance

  @behaviour Kadena.Types.Spec

  @type data :: map()
  @type provenance :: Provenance.t() | nil
  @type provenance_arg :: provenance() | list()
  @type value :: data() | provenance_arg()
  @type validation :: {:ok, value()} | {:error, Keyword.t()}

  @type t :: %__MODULE__{
          data: data(),
          provenance: provenance()
        }

  defstruct [:data, :provenance]

  @impl true
  def new(args) do
    data = Keyword.get(args, :data)
    provenance = Keyword.get(args, :provenance)

    with {:ok, data} <- validate_data(data),
         {:ok, provenance} <- validate_provenance(provenance) do
      %__MODULE__{data: data, provenance: provenance}
    end
  end

  @spec validate_data(data :: data()) :: validation()
  def validate_data(data) when is_map(data), do: {:ok, data}
  def validate_data(_data), do: {:error, [data: :invalid]}

  @spec validate_provenance(provenance :: provenance_arg()) :: validation()
  def validate_provenance(nil), do: {:ok, nil}
  def validate_provenance(%Provenance{} = provenance), do: {:ok, provenance}

  def validate_provenance(provenance) do
    case Provenance.new(provenance) do
      %Provenance{} = provenance -> {:ok, provenance}
      _error -> {:error, [provenance: :invalid]}
    end
  end
end