lib/which_build_steps_can_run.ex

defmodule BuildPipeline.WhichBuildStepsCanRun do
  def determine(%{runners: runners, mode: mode}) do
    runners
    |> concurrently_runable()
    |> modal_filter(mode)
    |> Enum.map(fn {pid, _} -> pid end)
  end

  defp modal_filter(runners, :verbose) do
    runners
  end

  defp modal_filter(runners, :normal) do
    runners
  end

  defp modal_filter(runners, :debug) do
    runners
    |> Enum.sort_by(&runner_order/1, &<=/2)
    |> case do
      [first | _] -> [first]
      _ -> []
    end
  end

  defp runner_order({_pid, %{order: order}}), do: order

  defp concurrently_runable(runners) do
    completed_runners = completed_runners_by_name(runners)

    Enum.filter(runners, fn {_, %{status: status, depends_on: depends_on}} ->
      status == :incomplete && MapSet.subset?(depends_on, completed_runners)
    end)
  end

  defp completed_runners_by_name(runners) do
    runners
    |> Enum.filter(fn {_, %{status: status}} -> status == :complete end)
    |> Enum.map(fn {_, %{build_step_name: build_step_name}} -> build_step_name end)
    |> MapSet.new()
  end
end