lib/ash/flow/verifiers/verify_return.ex

defmodule Ash.Flow.Verifiers.VerifyReturn do
  @moduledoc """
  Ensures that all steps in a return statement are returnable
  """
  use Spark.Dsl.Verifier

  alias Spark.Dsl.Verifier

  def verify(dsl_state) do
    steps = Ash.Flow.Info.steps(dsl_state)

    return_step_names =
      case Ash.Flow.Info.returns(dsl_state) do
        value when is_list(value) ->
          if Keyword.keyword?(value) do
            Keyword.keys(value)
          else
            value
          end

        value when is_map(value) ->
          Map.keys(value)

        value ->
          value
      end
      |> List.wrap()

    case Enum.find(return_step_names, fn step_name ->
           is_nil(returnable_step(steps, step_name))
         end) do
      nil ->
        :ok

      invalid_step_name ->
        module = Verifier.get_persisted(dsl_state, :module)

        {:error,
         Spark.Error.DslError.exception(
           module: module,
           path: [:flow, :returns, invalid_step_name],
           message: """
           #{inspect(invalid_step_name)} is not a returnable step in #{inspect(module)}.
           """
         )}
    end
  end

  defp returnable_step(steps, name) do
    Enum.find(steps, fn
      %{name: ^name} = step ->
        step

      %Ash.Flow.Step.Map{} ->
        nil

      %{steps: steps} ->
        returnable_step(steps, name)

      %{name: ^name} = step ->
        step

      _ ->
        nil
    end)
  end
end