Packages

ash

3.4.29
3.29.3 3.29.2 3.29.1 3.29.0 3.28.0 3.27.8 3.27.7 3.27.6 3.27.5 3.27.4 3.27.3 3.27.2 3.27.1 3.27.0 3.26.0 3.25.2 3.25.1 3.25.0 retired 3.24.7 3.24.6 3.24.5 3.24.4 3.24.3 3.24.2 3.24.1 3.24.0 3.23.1 3.23.0 3.22.2 3.22.1 3.22.0 3.21.3 3.21.2 3.21.1 3.21.0 3.20.0 3.19.3 3.19.2 3.19.1 3.19.0 3.18.0 3.17.1 3.17.0 3.16.0 3.15.0 3.14.1 3.14.0 retired 3.13.2 3.13.1 3.13.0 3.12.0 3.11.3 3.11.2 3.11.1 3.11.0 3.10.1 3.10.0 3.9.0 3.8.0 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0 retired 3.6.3 retired 3.6.2 3.6.1 3.6.0 3.5.43 3.5.42 3.5.41 3.5.40 3.5.39 3.5.38 3.5.37 3.5.36 3.5.35 3.5.34 3.5.33 3.5.32 3.5.31 3.5.30 3.5.29 3.5.28 3.5.27 3.5.26 3.5.25 3.5.24 3.5.23 3.5.22 3.5.21 3.5.20 3.5.19 3.5.18 3.5.17 3.5.16 3.5.15 3.5.14 3.5.13 3.5.12 3.5.11 3.5.10 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.74 retired 3.4.73 3.4.72 3.4.71 3.4.70 3.4.69 3.4.68 3.4.67 3.4.66 3.4.65 3.4.64 3.4.63 3.4.62 3.4.61 3.4.60 3.4.59 3.4.58 3.4.57 3.4.56 3.4.55 3.4.54 3.4.53 3.4.52 3.4.51 3.4.50 3.4.49 3.4.48 3.4.47 3.4.46 3.4.45 3.4.44 3.4.43 3.4.42 3.4.41 3.4.40 3.4.39 3.4.38 3.4.37 3.4.36 3.4.35 3.4.34 3.4.33 3.4.32 3.4.31 3.4.30 3.4.29 3.4.28 3.4.27 3.4.26 3.4.25 3.4.24 3.4.23 3.4.22 3.4.21 3.4.20 3.4.19 3.4.18 3.4.17 3.4.16 3.4.15 3.4.14 3.4.13 3.4.12 3.4.11 3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.3 3.3.2 3.3.1 3.3.0 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.16 3.0.15 3.0.14 3.0.13 3.0.12 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 3.0.0-rc.46 3.0.0-rc.45 3.0.0-rc.44 3.0.0-rc.43 3.0.0-rc.42 3.0.0-rc.41 3.0.0-rc.40 3.0.0-rc.39 3.0.0-rc.38 3.0.0-rc.37 3.0.0-rc.36 3.0.0-rc.35 3.0.0-rc.34 3.0.0-rc.33 3.0.0-rc.32 3.0.0-rc.31 3.0.0-rc.29 3.0.0-rc.28 3.0.0-rc.27 3.0.0-rc.26 3.0.0-rc.25 3.0.0-rc.24 3.0.0-rc.23 3.0.0-rc.22 3.0.0-rc.21 3.0.0-rc.20 3.0.0-rc.19 3.0.0-rc.18 3.0.0-rc.17 3.0.0-rc.16 3.0.0-rc.15 3.0.0-rc.14 3.0.0-rc.13 3.0.0-rc.12 3.0.0-rc.11 3.0.0-rc.10 3.0.0-rc.9 3.0.0-rc.8 3.0.0-rc.7 3.0.0-rc.6 3.0.0-rc.5 3.0.0-rc.4 3.0.0-rc.3 3.0.0-rc.1 3.0.0-rc.0 2.21.15 2.21.14 2.21.13 2.21.12 2.21.11 2.21.10 2.21.9 2.21.8 2.21.7 2.21.6 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.3 2.20.2 2.20.1 2.20.0 2.19.14 2.19.13 2.19.12 2.19.11 2.19.10 2.19.9 2.19.8 2.19.7 2.19.6 2.19.5 2.19.4 2.19.3 retired 2.19.2 retired 2.19.1 retired 2.19.0 retired 2.18.2 2.18.1 2.18.0 2.17.24 2.17.23 2.17.22 2.17.21 2.17.20 2.17.19 2.17.18 2.17.17 2.17.16 2.17.15 2.17.14 2.17.13 2.17.12 2.17.11 2.17.10 2.17.9 2.17.8 2.17.7 2.17.6 2.17.5 2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.1 2.16.0 2.15.20 2.15.19 2.15.18 2.15.17 2.15.16 2.15.15 2.15.14 2.15.13 2.15.12 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.2 2.15.1 2.15.0 2.14.21 2.14.20 2.14.19 2.14.18 2.14.17 2.14.16 2.14.15 2.14.14 2.14.13 2.14.12 2.14.11 2.14.10 2.14.9 2.14.8 2.14.7 2.14.6 2.14.5 2.14.4 2.14.3 2.14.2 2.14.1 2.14.0 2.13.4 retired 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.11 2.11.10 2.11.9 2.11.8 2.11.7 2.11.6 2.11.5 2.11.4 2.11.3 2.11.2 2.11.1 2.11.0 2.11.0-rc.3 2.11.0-rc.2 2.11.0-rc.1 2.11.0-rc.0 2.10.2 2.10.1 2.10.0 2.9.29 2.9.28 2.9.27 2.9.26 2.9.25 2.9.24 2.9.23 2.9.22 2.9.21 2.9.20 2.9.19 2.9.18 2.9.17 2.9.16 2.9.15 2.9.14 2.9.13 2.9.12 2.9.11 2.9.10 2.9.9 2.9.8 2.9.7 2.9.6 2.9.5 2.9.4 2.9.3 2.9.2 2.9.1 2.9.0 2.8.1 2.8.0 2.7.1 2.7.0 2.6.31 2.6.30 2.6.29 2.6.28 2.6.27 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.16 2.5.15 2.5.14 2.5.13 2.5.12 2.5.11 2.5.10 2.5.9 2.5.8 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.5.0-rc.6 2.5.0-rc.5 2.5.0-rc.4 2.5.0-rc.3 2.5.0-rc.2 2.5.0-rc.1 2.5.0-rc.0 2.4.30 2.4.29 2.4.28 2.4.27 2.4.26 2.4.25 2.4.24 2.4.23 2.4.22 2.4.21 2.4.20 2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 2.0.0-rc.15 2.0.0-rc.14 2.0.0-rc.13 2.0.0-rc.12 2.0.0-rc.11 2.0.0-rc.10 2.0.0-rc.9 2.0.0-rc.8 2.0.0-rc.7 2.0.0-rc.6 2.0.0-rc.5 2.0.0-rc.4 2.0.0-rc.3 2.0.0-rc.2 2.0.0-rc.1 2.0.0-rc.0 2.0.0-pre.8 2.0.0-pre.7 2.0.0-pre.6 2.0.0-pre.5 2.0.0-pre.4 2.0.0-pre.3 2.0.0-pre.2 2.0.0-pre.1 2.0.0-pre.0 1.53.3 1.53.2 1.53.0 1.52.0-rc.22 1.52.0-rc.21 1.52.0-rc.20 1.52.0-rc.19 1.52.0-rc.18 1.52.0-rc.17 1.52.0-rc.16 1.52.0-rc.15 1.52.0-rc.14 1.52.0-rc.13 1.52.0-rc.12 1.52.0-rc.11 1.52.0-rc.10 1.52.0-rc.9 1.52.0-rc.8 1.52.0-rc.7 1.52.0-rc.6 1.52.0-rc.5 1.52.0-rc.4 1.52.0-rc.3 1.52.0-rc.2 1.52.0-rc.1 1.52.0-rc.0 1.51.2 1.51.1 retired 1.51.0 1.50.21 1.50.20 1.50.19 1.50.18 1.50.17 1.50.16 1.50.15 1.50.14 1.50.13 1.50.12 1.50.11 1.50.10 1.50.9 1.50.8 1.50.7 1.50.6 1.50.5 1.50.4 1.50.3 1.50.2 1.50.1 1.50.0 1.49.0 1.48.0-rc.30 1.48.0-rc.29 1.48.0-rc.28 1.48.0-rc.27 1.48.0-rc.26 1.48.0-rc.25 1.48.0-rc.24 1.48.0-rc.23 1.48.0-rc.22 1.48.0-rc.21 1.48.0-rc.20 1.48.0-rc.19 1.48.0-rc.18 1.48.0-rc.17 1.48.0-rc.16 1.48.0-rc.15 1.48.0-rc.14 1.48.0-rc.13 1.48.0-rc.12 1.48.0-rc.11 1.48.0-rc.10 1.48.0-rc.9 1.48.0-rc.8 1.48.0-rc.7 1.48.0-rc.6 1.48.0-rc.5 1.48.0-rc.4 1.48.0-rc.3 1.48.0-rc.2 1.48.0-rc.1 1.48.0-rc.0 1.47.12 1.47.11 1.47.10 1.47.9 1.47.8 1.47.7 1.47.6 1.47.5 1.47.4 1.47.3 1.47.2 1.47.1 1.47.0 1.46.13 1.46.12 1.46.11 1.46.10 1.46.9 1.46.8 1.46.7 1.46.6 1.46.5 1.46.4 1.46.3 1.46.2 1.46.1 1.46.0 1.45.0-rc9 1.45.0-rc8 1.45.0-rc7 1.45.0-rc6 1.45.0-rc5 1.45.0-rc4 1.45.0-rc3 1.45.0-rc20 1.45.0-rc2 1.45.0-rc19 1.45.0-rc18 1.45.0-rc17 1.45.0-rc16 1.45.0-rc15 1.45.0-rc14 1.45.0-rc13 1.45.0-rc12 1.45.0-rc11 1.45.0-rc10 1.45.0-rc1 1.45.0-rc0 1.44.13 1.44.12 1.44.11 1.44.10 1.44.9 1.44.8 1.44.7 1.44.6 1.44.5 1.44.4 1.44.3 1.44.2 1.44.1 1.44.0 1.43.12 1.43.11 1.43.10 1.43.9 1.43.8 1.43.7 1.43.6 1.43.5 1.43.4 1.43.3 1.43.2 1.43.1 1.43.0 1.42.0 1.41.12 1.41.11 1.41.10 1.41.9 1.41.8 1.41.7 1.41.6 1.41.5 1.41.4 1.41.3 1.41.2 1.41.1 1.41.0 1.40.0 1.39.7 1.39.6 1.39.5 1.39.4 1.39.3 1.39.2 1.39.1 1.39.0 1.38.0 1.37.2 1.37.1 1.37.0 1.36.22 1.36.21 1.36.19 1.36.18 1.36.17 1.36.16 1.36.15 1.36.14 1.36.13 1.36.12 1.36.11 1.36.10 1.36.9 1.36.8 1.36.7 1.36.6 1.36.5 1.36.4 1.36.3 1.36.2 1.36.0 1.35.1 1.35.0 1.34.9 1.34.8 1.34.7 1.34.6 1.34.5 1.34.4 1.34.3 1.34.2 1.34.1 1.34.0 1.33.0 1.32.2 1.32.1 1.32.0 1.31.1 1.31.0 1.30.2 1.30.1 1.29.0-rc1 1.29.0-rc0 1.28.1 1.28.0 1.27.1 1.27.0 1.26.13 1.26.12 1.26.11 1.26.10 1.26.9 1.26.8 1.26.7 1.26.6 1.26.5 1.26.4 1.26.2 1.26.1 1.26.0 1.25.8 1.25.7 1.25.6 1.25.5 1.25.4 1.25.3 1.25.2 1.25.1 1.25.0 1.24.2 1.24.1 1.24.0 1.23.3 1.23.2 1.23.1 1.23.0 1.22.1 1.22.0 1.20.1 1.20.0 1.19.1 1.19.0 1.18.1 1.18.0 1.17.1 1.17.0 1.16.2 1.15.1 1.15.0 1.14.0 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.12.0 1.11.1 1.11.0 1.10.0 1.9.0 1.8.0 1.7.0 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.1 1.5.0 1.4.1 1.4.0 1.3.1 1.3.0 1.2.1 1.2.0 1.1.3 1.1.2 1.1.0 1.0.3 1.0.2 1.0.1 1.0.0 0.13.1 0.13.0 0.12.0 0.10.0 0.9.1 0.9.0 0.8.0 0.7.0 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.2 0.5.1 0.5.0 0.4.0 0.3.0 0.2.0 0.1.9 0.1.8 0.1.3 0.1.1 0.1.0

A declarative, extensible framework for building Elixir applications.

Security advisory: This version has known vulnerabilities. View advisories

Current section

Files

Jump to
ash lib ash query operator operator.ex
Raw

lib/ash/query/operator/operator.ex

defmodule Ash.Query.Operator do
@moduledoc """
An operator is a predicate with a `left` and a `right`
For more information on being a predicate, see `Ash.Filter.Predicate`. Most of the complexities
are there. An operator must meet both behaviours.
"""
alias Ash.Query.{Call, Expression, Not, Ref}
@doc """
Create a new predicate. There are various return types possible:
* `{:ok, left, right}` - Return the left/right values of the operator
* `{:ok, operator}` - Return the operator itself, this or the one above are acceptable
* `{:known, boolean}` - If the value is already known, e.g `1 == 1`
* `{:error, error}` - If there was an error creating the operator
"""
@callback new(term, term) ::
{:ok, term, term} | {:ok, term} | {:known, boolean} | {:error, term}
@doc """
The implementation of the inspect protocol.
If not defined, it will be inferred
"""
@callback to_string(struct, Inspect.Opts.t()) :: term
@doc """
Evaluates the operator in Elixir
"""
@callback evaluate(term) :: term
@doc """
If `true`, will be allowed to evaluate `nil` inputs.
If `false` (the default), any `nil` inputs will cause a `nil` return.
"""
@callback evaluate_nil_inputs?() :: boolean()
@doc """
The types accepted by the operator. Defaults to `[:same, :any]`, which is any values of the same type.
"""
@callback types() :: [
:any | :same | [Ash.Type.t() | {Ash.Type.t(), constraints :: Keyword.t()}]
]
@doc """
The types that the expression can return. Should be one entry in the list for each entry in `types`.
"""
@callback returns() :: [
:any | :same | Ash.Type.t() | {Ash.Type.t(), constraints :: Keyword.t()}
]
@callback predicate?() :: boolean()
@doc """
Whether or not the operator can evaluate to nil.
"""
@callback can_return_nil?(func :: map) :: boolean()
@doc "Evaluate the operator with provided inputs"
def evaluate(%mod{left: left, right: right} = op) when is_nil(left) or is_nil(right) do
if mod.evaluate_nil_inputs?() do
do_evaluate(op)
else
{:known, nil}
end
end
def evaluate(op) do
do_evaluate(op)
end
defp do_evaluate(%mod{left: left, right: right} = op) do
overloads = operator_overloads(mod.operator()) || []
overloads
|> Enum.find_value(fn {types, mod} ->
case Ash.Type.determine_types([types], [left, right]) do
[] ->
nil
_types ->
if function_exported?(mod, :evaluate_operator, 1) do
case mod.evaluate_operator(op) do
{:known, value} ->
{:known, value}
_ ->
nil
end
end
end
end)
|> case do
nil ->
mod.evaluate(op)
{:known, value} ->
{:known, value}
end
end
@doc "Create a new operator. Pass the module and the left and right values"
def new(mod, %Ref{} = left, right) do
try_cast_with_ref(mod, left, right)
end
def new(mod, left, %Ref{} = right) do
try_cast_with_ref(mod, left, right)
end
def new(mod, %{__struct__: struct} = left, right)
when struct in [Call, Expression, Not] do
mod.new(left, right)
end
def new(mod, left, %{__struct__: struct} = right)
when struct in [Call, Expression, Not] do
mod.new(left, right)
end
def new(mod, %{__predicate__?: _} = left, right) do
mod.new(left, right)
end
def new(mod, left, %{__predicate__?: _} = right) do
mod.new(left, right)
end
def new(mod, left, right) do
if Ash.Expr.expr?(left) or Ash.Expr.expr?(right) do
mod.new(left, right)
else
case mod.new(left, right) do
{:ok, val} ->
case val do
%mod{__predicate__?: _} ->
case mod.evaluate(val) do
{:known, value} -> {:ok, value}
{:error, error} -> {:error, error}
:unknown -> {:ok, val}
end
_ ->
{:ok, val}
end
{:error, error} ->
{:error, error}
end
end
end
@doc "Get type overloads for the given operator"
def operator_overloads(operator) do
:ash
|> Application.get_env(:known_types, [])
|> List.wrap()
|> Enum.reduce(%{}, fn type, acc ->
Code.ensure_compiled!(type)
if function_exported?(type, :operator_overloads, 0) do
Map.merge(acc, type.operator_overloads()[operator] || %{}, fn _, left, right ->
Map.merge(left, right, fn _, left, right ->
List.wrap(left) ++ List.wrap(right)
end)
end)
else
acc
end
end)
end
@doc false
def try_cast_with_ref(mod, left, right) do
Enum.find_value(mod.types(), fn type ->
try_cast(left, right, type)
end)
|> case do
nil ->
{:error,
Ash.Error.Query.InvalidFilterValue.exception(
value: struct(mod, left: left, right: right),
message: "No matching types. Possible types: #{inspect(mod.types())}"
)}
{:error, error} ->
{:error, error}
{:ok, left, right} ->
mod.new(left, right)
end
end
defp try_cast(%{__predicate__?: _} = left, right, _) do
{:ok, left, right}
end
defp try_cast(left, %{__predicate__?: _} = right, _) do
{:ok, left, right}
end
defp try_cast(%Ref{attribute: %{type: type, constraints: constraints}} = left, right, :same) do
if Ash.Expr.expr?(right) do
{:ok, left, right}
else
case Ash.Query.Type.try_cast(right, type, constraints) do
{:ok, new_right} ->
{:ok, left, new_right}
_ ->
nil
end
end
end
defp try_cast(left, %Ref{attribute: %{type: type, constraints: constraints}} = right, :same) do
if Ash.Expr.expr?(right) do
{:ok, left, right}
else
case Ash.Query.Type.try_cast(left, type, constraints) do
{:ok, new_left} ->
{:ok, new_left, right}
_ ->
nil
end
end
end
defp try_cast(%Ref{attribute: %{type: type, constraints: constraints}} = left, right, [
:any,
{:array, :same}
]) do
if Ash.Expr.expr?(right) do
{:ok, left, right}
else
case right do
%Ref{attribute: %{type: {:array, _type}}} ->
{:ok, left, right}
%Ref{} ->
nil
right ->
case Ash.Query.Type.try_cast(right, {:array, type}, items: constraints) do
{:ok, new_right} ->
{:ok, left, new_right}
_ ->
nil
end
end
end
end
defp try_cast(
left,
%Ref{attribute: %{type: {:array, type}, constraints: constraints}} = right,
[:any, {:array, :same}]
) do
if Ash.Expr.expr?(right) do
{:ok, left, right}
else
case Ash.Query.Type.try_cast(left, type, constraints) do
{:ok, new_left} ->
{:ok, new_left, right}
_ ->
nil
end
end
end
# We don't have a way to infer types from values right now
defp try_cast(left, right, [:same, :same]) do
{:ok, left, right}
end
defp try_cast(left, right, [:same, {:array, :same}]) do
{:ok, left, right}
end
defp try_cast(left, right, [{:array, :same}, :same]) do
{:ok, left, right}
end
defp try_cast(left, right, [{:array, :same}, {:array, :same}]) do
{:ok, left, right}
end
defp try_cast(left, right, [:same, type]) do
try_cast(left, right, [type, type])
end
defp try_cast(left, right, [type, :same]) do
try_cast(left, right, [type, type])
end
defp try_cast(left, right, [{:array, :same}, type]) do
try_cast(left, right, [{:array, type}, type])
end
defp try_cast(left, right, [type, :same]) do
try_cast(left, right, [type, type])
end
defp try_cast(left, right, [type, {:array, :same}]) do
try_cast(left, right, [type, {:array, type}])
end
defp try_cast(left, right, [left_type, right_type]) do
with {:ok, left} <- cast_one(left, left_type),
{:ok, right} <- cast_one(right, right_type) do
{:ok, left, right}
else
{:error, error} ->
{:error, error}
end
end
defp try_cast(left, right, :any) do
{:ok, left, right}
end
defp try_cast(left, right, {:array, :any}) do
{:ok, left, right}
end
defp try_cast(_, _, _), do: nil
defp cast_one(value, {:array, :any}) do
{:ok, value}
end
defp cast_one(value, :any) do
{:ok, value}
end
defp cast_one(value, {type, constraints}) do
if Ash.Expr.expr?(value) do
{:ok, value}
else
case Ash.Query.Type.try_cast(value, type, constraints) do
{:ok, casted} ->
{:ok, casted}
_ ->
{:error, "Could not cast #{inspect(value)} as #{inspect(type)}"}
end
end
end
defp cast_one(value, type) do
if Ash.Expr.expr?(value) do
{:ok, value}
else
case Ash.Query.Type.try_cast(value, type) do
{:ok, casted} ->
{:ok, casted}
_ ->
{:error, "Could not cast #{inspect(value)} as #{inspect(type)}"}
end
end
end
def operators do
[
Ash.Query.Operator.Eq,
Ash.Query.Operator.GreaterThanOrEqual,
Ash.Query.Operator.GreaterThan,
Ash.Query.Operator.In,
Ash.Query.Operator.IsNil,
Ash.Query.Operator.LessThanOrEqual,
Ash.Query.Operator.LessThan,
Ash.Query.Operator.NotEq
] ++ Ash.Query.Operator.Basic.operator_modules()
end
def operator_symbols do
Enum.map(operators(), & &1.operator())
end
defmacro __using__(opts) do
unless opts[:operator] do
raise "Operator is required!"
end
quote do
defstruct [
:left,
:right,
operator: unquote(opts[:operator]),
embedded?: false,
__operator__?: true,
__predicate__?: unquote(opts[:predicate?] || false)
]
if unquote(opts[:predicate?]) do
@behaviour Ash.Filter.Predicate
end
@behaviour Ash.Query.Operator
alias Ash.Query.Ref
import Inspect.Algebra
def operator, do: unquote(opts[:operator])
def name, do: unquote(opts[:name] || opts[:operator])
@impl Ash.Query.Operator
def predicate?, do: unquote(opts[:predicate?] || false)
@impl Ash.Query.Operator
if unquote(opts[:predicate?]) do
def returns do
Enum.map(List.wrap(unquote(opts[:types])), fn _ -> :boolean end)
end
else
def returns do
unquote(opts[:returns] || :unknown)
end
end
@impl Ash.Query.Operator
def types do
unquote(opts[:types] || [:same, :any])
end
@impl Ash.Query.Operator
def new(left, right), do: {:ok, struct(__MODULE__, left: left, right: right)}
@impl Ash.Query.Operator
def evaluate_nil_inputs?, do: false
@impl Ash.Query.Operator
def to_string(%{left: left, right: right, operator: operator}, opts) do
concat([
to_doc(left, opts),
" ",
to_string(operator),
" ",
to_doc(right, opts)
])
end
@impl Ash.Query.Operator
def can_return_nil?(_), do: true
defoverridable to_string: 2, new: 2, evaluate_nil_inputs?: 0, can_return_nil?: 1
defimpl Inspect do
def inspect(%mod{} = op, opts) do
mod.to_string(op, opts)
end
end
end
end
end