Current section
Files
Jump to
Current section
Files
lib/reads.ex
defmodule AlchemyTable.Reads do
@moduledoc false
alias AlchemyTable.Decoding
def parse_result(result, type_spec) do
result
|> Enum.map(fn {:ok, rows} -> rows.chunks end)
|> List.flatten()
|> group_by_row_key()
|> Enum.map(&parse_typed(type_spec, &1))
end
def parse_typed(type_spec, chunks) do
initial = %{last_family: nil, parsed: %{}}
%{parsed: parsed} =
Enum.reduce(chunks, initial, fn chunk, %{last_family: last_family, parsed: parsed} ->
family_key =
case is_map(chunk.family_name) do
false -> last_family
true -> String.to_atom(chunk.family_name.value)
end
family_spec = Map.fetch!(type_spec, family_key)
column_name = chunk.qualifier.value
column_value = chunk.value
next_parsed = parse_from_spec(family_spec, column_name, column_value, parsed)
%{parsed: next_parsed, last_family: family_key}
end)
parsed
end
def parse_from_spec(type_spec, field_name, value, accum) do
case String.contains?(field_name, ".") do
true ->
[parent_name | rest] = String.split(field_name, ".")
parent_key = String.to_atom(parent_name)
prev_child = Map.get(accum, parent_key, %{})
child_type_spec = Map.fetch!(type_spec, parent_key)
child_qualifier = Enum.join(rest, ".")
child_value = parse_from_spec(child_type_spec, child_qualifier, value, prev_child)
Map.put(accum, parent_key, child_value)
false ->
key = String.to_atom(field_name)
type = Map.get(type_spec, key)
value = Decoding.decode(type, value)
Map.put(accum, key, value)
end
end
def group_by_row_key(rows) do
initial = %{rows: [], row_key: ""}
rows
|> Enum.reduce(initial, &add_to_group/2)
|> Map.fetch!(:rows)
|> Enum.map(&Enum.reverse/1)
end
defp add_to_group(%{row_key: row_key} = chunk, accum) do
%{rows: prev_rows, row_key: prev_row_key} = accum
{head, _} = List.pop_at(prev_rows, 0, [])
case new_row?(row_key, prev_row_key) do
true ->
next_rows = List.insert_at(prev_rows, 0, [chunk])
%{rows: next_rows, row_key: row_key}
false ->
next_rows = List.replace_at(prev_rows, 0, [chunk | head])
%{accum | rows: next_rows}
end
end
defp new_row?(next_key, prev_key), do: next_key not in ["", prev_key]
end