lib/ash/resource/transformers/validate_accept.ex

defmodule Ash.Resource.Transformers.ValidateAccept do
  @moduledoc "Validates that accept and reject lists only contain valid attributes"
  use Spark.Dsl.Transformer

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

  @impl true
  def after_compile?, do: true

  @impl true
  def transform(dsl_state) do
    {private_attributes, public_attributes} =
      dsl_state
      |> Transformer.get_entities([:attributes])
      |> Enum.split_with(& &1.private?)

    public_attribute_names = MapSet.new(public_attributes, & &1.name)
    private_attribute_names = MapSet.new(private_attributes, & &1.name)

    Transformer.get_entities(dsl_state, [:actions])
    |> Enum.each(fn
      %{name: action_name, accept: accept, reject: reject} ->
        validate_attribute_name = fn attribute_name, type ->
          cond do
            MapSet.member?(private_attribute_names, attribute_name) ->
              raise DslError,
                path: [:actions, action_name, type, attribute_name],
                message: "#{attribute_name} is a private attribute"

            MapSet.member?(public_attribute_names, attribute_name) ->
              :ok

            true ->
              raise DslError,
                path: [:actions, action_name, type, attribute_name],
                message: "#{attribute_name} is not an attribute"
          end
        end

        Enum.each(
          accept,
          &validate_attribute_name.(
            &1,
            :accept
          )
        )

        Enum.each(
          reject,
          &validate_attribute_name.(
            &1,
            :reject
          )
        )

      # read types do not have accept / reject fields
      %{type: :read} ->
        :ok
    end)

    :ok
  end
end