lib/edgedb/result.ex

defmodule EdgeDB.Result do
  @moduledoc """
  A structure that contains information related to the query result.

  It's mostly used in driver internally, but user can retrive it along with `EdgeDB.Query` struct
    from succeed query execution using `:raw` option for `EdgeDB.query*/4` functions. See `t:EdgeDB.query_option/0`.
  """

  alias EdgeDB.Protocol.Enums

  defstruct [
    :cardinality,
    :required,
    set: [],
    statement: nil
  ]

  @typedoc """
  A structure that contains information related to the query result.

  Fields:

    * `:statement` - EdgeQL statement that was executed.
    * `:required` - flag specifying that the result should not be empty.
    * `:set` - query result.
    * `:cardinality` - the expected number of elements in the returned set as a result of the query.
  """
  @type t() :: %__MODULE__{
          statement: String.t() | nil,
          required: boolean(),
          set: EdgeDB.Set.t() | list(binary()),
          cardinality: Enums.cardinality()
        }

  @doc """
  Process the result and extract the data.
  """
  @spec extract(t()) ::
          {:ok, EdgeDB.Set.t() | term() | :done}
          | {:error, Exception.t()}

  def extract(%__MODULE__{set: data}) when is_list(data) do
    {:error, EdgeDB.InterfaceError.new("result hasn't been decoded yet")}
  end

  def extract(%__MODULE__{cardinality: :at_most_one, required: required, set: set}) do
    if EdgeDB.Set.empty?(set) and required do
      {:error, EdgeDB.NoDataError.new("expected result, but query did not return any data")}
    else
      value =
        set
        |> Enum.take(1)
        |> List.first()

      {:ok, value}
    end
  end

  def extract(%__MODULE__{cardinality: :many, set: %EdgeDB.Set{} = set}) do
    {:ok, set}
  end

  def extract(%__MODULE__{cardinality: :no_result, required: true}) do
    {:error, EdgeDB.InterfaceError.new("query does not return data")}
  end

  def extract(%__MODULE__{cardinality: :no_result}) do
    {:ok, :executed}
  end
end