lib/ash/resource/transformers/cache_primary_key.ex

defmodule Ash.Resource.Transformers.CachePrimaryKey do
  @moduledoc "Validates and caches the primary key of a resource"
  use Spark.Dsl.Transformer

  alias Spark.Dsl.Transformer
  alias Spark.Error.DslError

  def transform(dsl_state) do
    primary_key_attribute =
      dsl_state
      |> Transformer.get_entities([:attributes])
      |> Enum.filter(& &1.primary_key?)

    pk_allows_nil? = Enum.any?(primary_key_attribute, & &1.allow_nil?)
    simple_equality? = Enum.all?(primary_key_attribute, &Ash.Type.simple_equality?(&1.type))

    primary_key =
      if pk_allows_nil? == false do
        Enum.map(primary_key_attribute, & &1.name)
      else
        :error_pk_allows_nil
      end

    case primary_key do
      :error_pk_allows_nil ->
        {:error, DslError.exception(message: "Primary keys must not be allowed to be nil")}

      [] ->
        {:error,
         DslError.exception(message: "Resources without a primary key are not yet supported")}

      [field] ->
        dsl_state =
          dsl_state
          |> Transformer.persist(:primary_key, [field])
          |> Transformer.persist(:primary_key_simple_equality?, simple_equality?)

        {:ok, dsl_state}

      fields ->
        dsl_state =
          dsl_state
          |> Transformer.persist(:primary_key, fields)
          |> Transformer.persist(:primary_key_simple_equality?, simple_equality?)

        {:ok, dsl_state}
    end
  end

  def after?(Ash.Resource.Transformers.BelongsToAttribute), do: true
  def after?(Ash.Resource.Transformers.DefaultPrimaryKey), do: true

  def after?(_), do: false
end