defmodule Quarto.Ecto.CursorFields do
@moduledoc """
Derive the cursor fields from a given queryable
"""
@type field :: atom()
@type position :: pos_integer()
@type direction :: :asc | :desc
@doc """
build/2 accepts an Ecto.Query and inspects the Ecto AST to retrieve
the fields the query will order by. These will be used to create the
cursor.
"""
@spec build(queryable :: Ecto.Query.t(), config :: Keyword.t()) :: [
{field(), {position(), direction()}}
]
def build(%Ecto.Query{order_bys: order_bys} = queryable, _config) do
Enum.map(order_bys, fn %Ecto.Query.QueryExpr{expr: expr} ->
case expr do
[{direction, {{:., [], [{:&, [], [position]}, field]}, [], []}}] ->
{field, {position, direction}}
[{_direction, {:fragment, [], _ast}}] ->
raise ArgumentError, "Fragments not supported currently, got: #{inspect(queryable)}"
[{_direction, _unsupported}] ->
raise ArgumentError,
"Unsupported clause passed into order by expr #{inspect(queryable)}"
end
end)
end
def build(unknown, _config) do
raise ArgumentError, "Expecting an `%Ecto.Query{}` struct, got: #{inspect(unknown)}"
end
end