lib/chore_runner/dsl.ex

defmodule ChoreRunner.DSL do
  @moduledoc """
  Macros which enable the chore DSL
  """

  def parse_result_handler(nil), do: & &1

  def parse_result_handler({:fn, _, _} = fun) do
    Macro.escape(fun)
  end

  def parse_result_handler(handler) when is_function(handler, 1) do
    handler
  end

  def parse_result_handler({m, f, a}) do
    if function_exported?(m, f, a) do
      fn chore -> apply(m, f, [chore]) end
    else
      raise "Function not exported for {#{m}, #{f}, #{a}}"
    end
  end

  def parse_result_handler(handler) do
    raise "result_handler must be an anonymous function, function capture, MFA of a single arity function, or nil. Got: #{inspect(handler)}"
  end

  def using(opts) do
    quote do
      alias ChoreRunner.Chore
      @behaviour Chore

      import ChoreRunner.Reporter,
        only: [
          report_failed: 1,
          report_failed: 2,
          log: 1,
          log: 2,
          set_counter: 2,
          set_counter: 3,
          inc_counter: 2,
          inc_counter: 3,
          get_reporter: 0
        ]

      import ChoreRunner.Input,
        only: [
          string: 2,
          int: 2,
          float: 2,
          file: 2,
          bool: 2,
          string: 1,
          int: 1,
          float: 1,
          file: 1,
          bool: 1
        ]

      def create_download(filename, opts) do
        opts = Keyword.put_new_lazy(opts, :chore_id, fn -> Process.get(:chore_id) end)

        filename
        |> ChoreRunner.Downloads.create_download(opts)
        |> tap(fn
          {:ok, download} ->
            ChoreRunner.Reporter.register_download(download)

            case Process.get(:chore_pubsub) do
              nil ->
                :noop

              pubsub ->
                Phoenix.PubSub.broadcast(
                  pubsub,
                  ChoreRunner.downloads_pubsub_topic(),
                  :downloads_updated
                )
            end

          _ ->
            :noop
        end)
      end

      def restriction, do: :self
      def inputs, do: []

      def validate_input(input),
        do: Chore.validate_input(%Chore{mod: __MODULE__}, input)

      def result_handler(chore) do
        result_handler = unquote(__MODULE__).parse_result_handler(unquote(opts)[:result_handler])
        result_handler.(chore)
      end

      defoverridable inputs: 0, restriction: 0, result_handler: 1
    end
  end
end