defmodule Ash.Query.Operator.In do
@moduledoc """
left in [1, 2, 3]
this predicate matches if the left is in the list on the right
For comparison, this simplifies to a set of "or equals", e.g
`{:or, {:or, {:or, left == 1}, left == 2}, left == 3}`
"""
use Ash.Query.Operator,
operator: :in,
predicate?: true,
types: [[:any, {:array, :same}]]
@inspect_items_limit 10
@dialyzer {:nowarn_function, compare: 2}
def new(%Ash.Query.Ref{} = left, right) when is_list(right) do
{:ok, %__MODULE__{left: left, right: MapSet.new(right)}}
end
def new(left, right), do: {:ok, %__MODULE__{left: left, right: right}}
def evaluate(%{left: nil}), do: {:known, nil}
def evaluate(%{right: nil}), do: {:known, nil}
def evaluate(%{left: left, right: right}) do
{:known, Enum.any?(right, &Comp.equal?(&1, left))}
end
def compare(%__MODULE__{left: left, right: %MapSet{} = left_right}, %__MODULE__{
left: left,
right: %MapSet{} = right_right
}) do
if MapSet.equal?(left_right, right_right) do
:mutually_inclusive
else
if MapSet.disjoint?(left_right, right_right) do
:mutually_exclusive
else
:unknown
end
end
end
def compare(%__MODULE__{}, %Ash.Query.Operator.Eq{
right: %Ref{}
}),
do: false
def compare(%__MODULE__{left: left, right: %MapSet{} = left_right}, %Ash.Query.Operator.Eq{
left: left,
right: value
}) do
if MapSet.member?(left_right, value) do
:left_implies_right
else
:mutually_exclusive
end
end
def compare(_, _), do: :unknown
def to_string(%{left: left, right: %MapSet{} = mapset}, opts) do
import Inspect.Algebra
list_doc =
case Enum.split(mapset, @inspect_items_limit) do
{left, []} -> to_doc(left, opts)
{left, _} -> concat(to_doc(left, opts), "...")
end
concat([
to_doc(left, opts),
" in ",
list_doc
])
end
def to_string(op, opts), do: super(op, opts)
end