Packages
ash
3.25.2
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
Current section
Files
lib/ash/query/operator/operator.ex
# SPDX-FileCopyrightText: 2019 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
#
# SPDX-License-Identifier: MIT
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{} = op) do
case find_overload(op) do
{type_mod, _overload_types} ->
if function_exported?(type_mod, :evaluate_operator, 1) do
case type_mod.evaluate_operator(op) do
{:known, value} ->
{:known, value}
_ ->
mod.evaluate(op)
end
else
mod.evaluate(op)
end
nil ->
mod.evaluate(op)
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 """
Finds the matching overload for an operator based on the resolved types of its arguments.
Returns `{type_mod, overload_types}` or `nil`.
"""
def find_overload(%mod{left: left, right: right} = _op) do
overloads = operator_overloads(mod.operator()) || %{}
if overloads == %{} do
nil
else
{resolved_types, _returns} = Ash.Expr.determine_types(mod, [left, right])
Enum.find_value(overloads, fn {overload_types, type_mod} ->
if overload_types_match?(overload_types, resolved_types) do
type_mod =
case type_mod do
{_, type_mod} -> type_mod
type_mod -> type_mod
end
{type_mod, overload_types}
end
end)
end
end
defp overload_types_match?(overload_types, resolved_types)
when is_list(overload_types) and is_list(resolved_types) and
length(overload_types) == length(resolved_types) do
overload_types
|> Enum.zip(resolved_types)
|> Enum.all?(fn
{_, nil} ->
false
{declared, {resolved, _constraints}} ->
Ash.Type.get_type(declared) == Ash.Type.get_type(resolved)
{declared, resolved} ->
Ash.Type.get_type(declared) == Ash.Type.get_type(resolved)
end)
end
defp overload_types_match?(_, _), do: false
@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 ->
List.wrap(left) ++ List.wrap(right)
end)
else
acc
end
end)
end
@doc """
Finds a custom expression module for an overloaded operator, if one exists.
Checks all known types that define `operator_overloads/0` for the given operator,
and if any matching type also defines `operator_expression/1`, calls it with the
operator struct. Returns `{:ok, custom_expression_module}` or `:unknown`.
"""
def operator_expression(op) do
case find_overload(op) do
{type_mod, _overload_types} ->
if function_exported?(type_mod, :operator_expression, 1) do
case type_mod.operator_expression(op) do
{:ok, expr_module} -> {:ok, expr_module}
:unknown -> :unknown
end
else
:unknown
end
nil ->
:unknown
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
if !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