defmodule Adbc.Result do
@moduledoc """
A struct returned as result from queries.
It has two fields:
* `:data` - a list of `Adbc.Column`
* `:num_rows` - the number of rows returned, if returned
by the database
"""
defstruct [:num_rows, :data]
@type t :: %Adbc.Result{
num_rows: non_neg_integer() | nil,
data: [%Adbc.Column{}]
}
@doc """
`materialize/1` converts the result set's data from reference type to regular Elixir terms.
"""
@spec materialize(%Adbc.Result{} | {:ok, %Adbc.Result{}} | {:error, String.t()}) ::
%Adbc.Result{} | {:ok, %Adbc.Result{}} | {:error, String.t()}
def materialize(%Adbc.Result{data: data} = result) when is_list(data) do
%{result | data: Enum.map(data, &Adbc.Column.materialize/1)}
end
# allow for the result to be wrapped in an `{:ok, result}` tuple
# and also allow error tuples to pass through
# easier to use in pipelines
def materialize({:ok, %Adbc.Result{data: data} = result}) when is_list(data) do
{:ok, %{result | data: Enum.map(data, &Adbc.Column.materialize/1)}}
end
def materialize({:error, reason}), do: {:error, reason}
@doc """
Returns a map of columns as a result.
"""
def to_map(result = %Adbc.Result{}) do
Map.new(to_list(materialize(result)).data, fn %Adbc.Column{name: name, type: type, data: data} ->
case type do
:list -> {name, Enum.map(data, &list_to_map/1)}
_ -> {name, data}
end
end)
end
@doc """
Convert any list view in the result set to normal lists.
"""
@spec to_list(%Adbc.Result{}) :: %Adbc.Result{}
def to_list(result = %Adbc.Result{data: data}) when is_list(data) do
%{result | data: Enum.map(data, &Adbc.Column.to_list/1)}
end
defp list_to_map(nil), do: nil
defp list_to_map(%Adbc.Column{name: name, type: type, data: data}) do
case type do
:list ->
list = Enum.map(data, &list_to_map/1)
if name == "item" do
list
else
{name, list}
end
:struct ->
Enum.map(data, &list_to_map/1)
_ ->
if name == "item" do
data
else
{name, data}
end
end
end
end