lib/bsv/contract/op_code_helpers.ex

defmodule BSV.Contract.OpCodeHelpers do
  @moduledoc """
  Helper module for using Op Codes in `BSV.Contract` modules.

  All known Op Codes are available as a function which simply pushes the Op Code
  word onto the Contract Script. Refer to `BSV.VM` for descriptions of each
  Op Code.

  In addition, `op_if/2` and `op_if/3` provide a more syntactically pleasing way
  of handling the flow controw operations by passing `handle_if` and `handle_else`
  callback functions.

      op_if(contract, &op_1add/1, &op_1sub/1)

      # Equivalent to...

      contract
      |> op_if()
      |> op_1add()
      |> op_else()
      |> op_1sub()
      |> op_endif()

  *The same applies to `op_notif/2` and `op_notif/3`*.
  """
  alias BSV.{Contract, OpCode}

  # Iterrates over all opcodes
  # Defines a function to push the specified opcode onto the contract script
  Enum.each(OpCode.all(), fn {op, _} ->
    key = op
    |> Atom.to_string()
    |> String.downcase()
    |> String.to_atom()

    @doc "Pushes the `#{op}` word onto the script."
    @spec unquote(key)(Contract.t()) :: Contract.t()
    def unquote(key)(%Contract{} = contract) do
      Contract.script_push(contract, unquote(op))
    end
  end)

  @doc """
  Wraps the given `handle_if` function with `OP_IF` and `OP_ENDIF` script words.
  """
  @spec op_if(Contract.t(), (Contract.t() -> Contract.t())) :: Contract.t()
  def op_if(%Contract{} = contract, handle_if) when is_function(handle_if) do
    contract
    |> op_if()
    |> handle_if.()
    |> op_endif()
  end

  @doc """
  Wraps the given `handle_if` and `handle_else` functions with `OP_IF`,
  `OP_ELSE` and `OP_ENDIF` script words.
  """
  @spec op_if(
    Contract.t(),
    (Contract.t() -> Contract.t()),
    (Contract.t() -> Contract.t())
  ) :: Contract.t()
  def op_if(%Contract{} = contract, handle_if, handle_else)
    when is_function(handle_if) and is_function(handle_else)
  do
    contract
    |> op_if()
    |> handle_if.()
    |> op_else()
    |> handle_else.()
    |> op_endif()
  end

  @doc """
  Wraps the given `handle_if` function with `OP_NOTIF` and `OP_ENDIF` script
  words.
  """
  @spec op_notif(Contract.t(), (Contract.t() -> Contract.t())) :: Contract.t()
  def op_notif(%Contract{} = contract, handle_if) when is_function(handle_if) do
    contract
    |> op_notif()
    |> handle_if.()
    |> op_endif()
  end

  @doc """
  Wraps the given `handle_if` and `handle_else` functions with `OP_NOTIF`,
  `OP_ELSE` and `OP_ENDIF` script words.
  """
  @spec op_notif(
    Contract.t(),
    (Contract.t() -> Contract.t()),
    (Contract.t() -> Contract.t())
  ) :: Contract.t()
  def op_notif(%Contract{} = contract, handle_if, handle_else)
    when is_function(handle_if) and is_function(handle_else)
  do
    contract
    |> op_notif()
    |> handle_if.()
    |> op_else()
    |> handle_else.()
    |> op_endif()
  end

end