lib/ash/filter/template_helpers.ex

defmodule Ash.Filter.TemplateHelpers do
  @moduledoc "Helpers for building filter templates"

  @deprecated "Use `expr?/1` instead, which is not a guard"
  defguard is_expr(value)
           when is_struct(value, Ash.Query.Not) or is_struct(value, Ash.Query.BooleanExpression) or
                  is_struct(value, Ash.Query.Call) or is_struct(value, Ash.Query.Ref) or
                  is_struct(value, Ash.Query.Exists) or
                  is_struct(value, Ash.Query.Parent) or
                  (is_struct(value) and is_map_key(value, :__predicate__?))

  def expr?(value)
      when is_struct(value, Ash.Query.Not) or is_struct(value, Ash.Query.BooleanExpression) or
             is_struct(value, Ash.Query.Call) or is_struct(value, Ash.Query.Ref) or
             is_struct(value, Ash.Query.Exists) or
             is_struct(value, Ash.Query.Parent) or
             (is_struct(value) and is_map_key(value, :__predicate__?)) do
    true
  end

  def expr?(value) when is_list(value) do
    Enum.any?(value, &expr?/1)
  end

  def expr?(value) when is_map(value) do
    value
    |> Map.keys()
    |> Enum.any?(&expr?/1)
  end

  def expr?({left, right}) do
    expr?(left) || expr?(right)
  end

  def expr?(tuple) when is_tuple(tuple) do
    tuple |> Tuple.to_list() |> expr?()
  end

  def expr?(_), do: false

  @doc "A helper for using actor values in filter templates"
  def actor(value), do: {:_actor, value}

  @doc "A helper for using action arguments in filter templates"
  def arg(name), do: {:_arg, name}

  @doc "A helper for creating a reference"
  def ref(name), do: {:_ref, [], name}

  @doc "A helper for creating a reference to a related path"
  def ref(path, name), do: {:_ref, path, name}

  @doc """
  A helper for using query context in filter templates

  An atom will just get the key, and a list will be accessed via `get_in`.
  """
  def context(name), do: {:_context, name}

  @doc "A helper for building an expression style filter"
  defmacro expr(expr) do
    quote do
      require Ash.Expr

      Ash.Expr.expr(unquote(expr))
    end
  end
end