2021-12-21 16:19:24 +13:00
|
|
|
defmodule AshPostgres.Types do
|
|
|
|
@moduledoc false
|
|
|
|
|
|
|
|
alias Ash.Query.Ref
|
|
|
|
|
|
|
|
def parameterized_type({:array, type}, constraints) do
|
|
|
|
{:array, parameterized_type(type, constraints[:items] || [])}
|
|
|
|
end
|
|
|
|
|
|
|
|
def parameterized_type(Ash.Type.CiString, constraints) do
|
|
|
|
parameterized_type(Ash.Type.CiStringWrapper, constraints)
|
|
|
|
end
|
|
|
|
|
|
|
|
def parameterized_type(type, constraints) do
|
|
|
|
if Ash.Type.ash_type?(type) do
|
2022-02-15 05:39:50 +13:00
|
|
|
if Ash.Type.cast_in_query?(type) do
|
|
|
|
parameterized_type(Ash.Type.ecto_type(type), constraints)
|
|
|
|
else
|
|
|
|
:any
|
|
|
|
end
|
2021-12-21 16:19:24 +13:00
|
|
|
else
|
|
|
|
if is_atom(type) && :erlang.function_exported(type, :type, 1) do
|
2022-01-25 11:59:31 +13:00
|
|
|
{:parameterized, type, constraints || []}
|
2021-12-21 16:19:24 +13:00
|
|
|
else
|
|
|
|
type
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def determine_types(mod, values) do
|
|
|
|
Code.ensure_compiled(mod)
|
|
|
|
|
|
|
|
cond do
|
|
|
|
:erlang.function_exported(mod, :types, 0) ->
|
|
|
|
mod.types()
|
|
|
|
|
|
|
|
:erlang.function_exported(mod, :args, 0) ->
|
|
|
|
mod.args()
|
|
|
|
|
|
|
|
true ->
|
|
|
|
[:any]
|
|
|
|
end
|
|
|
|
|> Enum.map(fn types ->
|
|
|
|
case types do
|
|
|
|
:same ->
|
|
|
|
types =
|
|
|
|
for _ <- values do
|
|
|
|
:same
|
|
|
|
end
|
|
|
|
|
|
|
|
closest_fitting_type(types, values)
|
|
|
|
|
|
|
|
:any ->
|
|
|
|
for _ <- values do
|
|
|
|
:any
|
|
|
|
end
|
|
|
|
|
|
|
|
types ->
|
|
|
|
closest_fitting_type(types, values)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|> Enum.min_by(fn types ->
|
|
|
|
types
|
|
|
|
|> Enum.map(&vagueness/1)
|
|
|
|
|> Enum.sum()
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp closest_fitting_type(types, values) do
|
|
|
|
types_with_values = Enum.zip(types, values)
|
|
|
|
|
|
|
|
types_with_values
|
|
|
|
|> fill_in_known_types()
|
|
|
|
|> clarify_types()
|
|
|
|
end
|
|
|
|
|
|
|
|
defp clarify_types(types) do
|
|
|
|
basis =
|
|
|
|
types
|
|
|
|
|> Enum.map(&elem(&1, 0))
|
|
|
|
|> Enum.min_by(&vagueness(&1))
|
|
|
|
|
|
|
|
Enum.map(types, fn {type, _value} ->
|
|
|
|
replace_same(type, basis)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp replace_same({:in, type}, basis) do
|
|
|
|
{:in, replace_same(type, basis)}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp replace_same(:same, :same) do
|
|
|
|
:any
|
|
|
|
end
|
|
|
|
|
|
|
|
defp replace_same(:same, {:in, :same}) do
|
|
|
|
{:in, :any}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp replace_same(:same, basis) do
|
|
|
|
basis
|
|
|
|
end
|
|
|
|
|
|
|
|
defp replace_same(other, _basis) do
|
|
|
|
other
|
|
|
|
end
|
|
|
|
|
|
|
|
defp fill_in_known_types(types) do
|
|
|
|
Enum.map(types, &fill_in_known_type/1)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp fill_in_known_type(
|
|
|
|
{vague_type, %Ref{attribute: %{type: type, constraints: constraints}}} = ref
|
|
|
|
)
|
|
|
|
when vague_type in [:any, :same] do
|
|
|
|
if Ash.Type.ash_type?(type) do
|
2022-02-02 11:44:01 +13:00
|
|
|
type = type |> parameterized_type(constraints) |> array_to_in()
|
2021-12-21 16:19:24 +13:00
|
|
|
{type, ref}
|
|
|
|
else
|
|
|
|
type =
|
|
|
|
if is_atom(type) && :erlang.function_exported(type, :type, 1) do
|
|
|
|
{:parameterized, type, []} |> array_to_in()
|
|
|
|
else
|
|
|
|
type |> array_to_in()
|
|
|
|
end
|
|
|
|
|
|
|
|
{type, ref}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp fill_in_known_type(
|
|
|
|
{{:array, type}, %Ref{attribute: %{type: {:array, type}} = attribute} = ref}
|
|
|
|
) do
|
|
|
|
{:in, fill_in_known_type({type, %{ref | attribute: %{attribute | type: type}}})}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp fill_in_known_type({type, value}), do: {array_to_in(type), value}
|
|
|
|
|
|
|
|
defp array_to_in({:array, v}), do: {:in, array_to_in(v)}
|
|
|
|
|
|
|
|
defp array_to_in({:parameterized, type, constraints}),
|
|
|
|
do: {:parameterized, array_to_in(type), constraints}
|
|
|
|
|
|
|
|
defp array_to_in(v), do: v
|
|
|
|
|
|
|
|
defp vagueness({:in, type}), do: vagueness(type)
|
|
|
|
defp vagueness(:same), do: 2
|
|
|
|
defp vagueness(:any), do: 1
|
|
|
|
defp vagueness(_), do: 0
|
|
|
|
end
|