defmodule Draft.Type.List do
@behaviour Draft.Type.Behaviour
@impl Draft.Type.Behaviour
def cast(nil, _opts) do
{:ok, nil}
end
@impl Draft.Type.Behaviour
def cast(value, type) when is_list(value) and is_atom(type) do
cast(value, type: type)
end
@impl Draft.Type.Behaviour
def cast(value, opts) when is_list(value) and is_list(opts) do
case Keyword.fetch(opts, :type) do
{:ok, type} ->
{typename, typeopts} =
case type do
{typename, typeopts} ->
{typename, typeopts}
typename when is_atom(typename) ->
{typename, []}
end
Draft.Registry.type(typename)
|> cast_values(value, typeopts)
_ ->
{:ok, value}
end
end
@impl Draft.Type.Behaviour
def cast(_value, _opts) do
{:error, ["invalid list"]}
end
defp cast_values(type, values, opts) do
values
|> Enum.with_index()
|> Enum.reduce({:ok, []}, fn {value, index}, acc ->
case acc do
{:ok, values} ->
case type.cast(value, opts) do
{:ok, valid_value} ->
{:ok, values ++ [valid_value]}
{:error, reason} ->
{:error, [{index, reason}]}
end
error ->
error
end
end)
end
@impl Draft.Type.Behaviour
def dump(val, opts \\ [])
@impl Draft.Type.Behaviour
def dump(nil, _opts) do
{:ok, nil}
end
@impl Draft.Type.Behaviour
def dump(values, opts) do
case Keyword.fetch(opts, :type) do
{:ok, {typename, typeopts}} when is_atom(typename) ->
type = Draft.Registry.type(typename)
values
|> Enum.with_index()
|> Enum.reduce_while({:ok, []}, fn {val, index}, acc ->
{:ok, dumped} = acc
case type.dump(val, typeopts) do
{:ok, val} ->
{:cont, {:ok, dumped ++ [val]}}
{:error, value} ->
{:halt, {:error, [{index, value}]}}
end
end)
_ ->
{:ok, values}
end
end
end