defprotocol Table.Reader do
@moduledoc """
A protocol to read tabular data.
"""
@typedoc """
Describes row-based traversal.
The enumerable should yield rows, where each row is a series of
values. The values should follow the order of columns in the
metadata.
"""
@type row_reader :: {:rows, metadata(), Enumerable.t()}
@typedoc """
Describes column-based traversal.
The enumerable should yield columns, where each column is a series
of values. The columns should have the same order as columns in the
metadata.
"""
@type column_reader :: {:columns, metadata(), Enumerable.t()}
@typedoc """
Table metadata.
User-specific metadata can be added in the form of namespaced
tuples as keys. The first element is the namespace key, typically an
atom, and the second is any term. The value can also be anything.
"""
@type metadata :: %{
required(:columns) => list(Table.column()),
optional(:count) => non_neg_integer(),
optional({term(), term()}) => any()
}
@doc """
Returns information on how to traverse the given tabular data.
There are generally two distinct ways of traversing tabular data,
that is, a row-based one and a column-based one. Depending on the
underlying data representation, one of them is more natural and
more efficient.
The `init/1` return value describes either of the two traversal
types, see `t:row_reader/0` and `t:column_reader/0` respectively.
Some structs may be tabular only in a subset of cases, therefore
`:none` may be returned to indicate that there is no valid data to
read.
The `init/1` function should not initiate any form of traversal
or open existing resources. If any setup is necessary, it should
be peformed when the underlying row or column enumerables are
traversed.
"""
@spec init(t()) :: row_reader() | column_reader() | :none
def init(tabular)
end
defimpl Table.Reader, for: List do
def init(list) do
with :none <- Table.Reader.Enumerable.init_columns(list),
:none <- Table.Reader.Enumerable.init_rows(list),
do: :none
end
end
defimpl Table.Reader, for: Map do
def init(map) do
Table.Reader.Enumerable.init_columns(map)
end
end