defmodule Fussy.Validators.Map do
@behaviour Fussy.Validator
alias Fussy.Utils
alias Fussy.ValidationError
defstruct [:key, :value]
@opaque t :: %__MODULE__{}
@spec new(Fussy.Validator.t(), Fussy.Validator.t()) :: __MODULE__.t()
def new(key, value) do
%__MODULE__{key: key, value: value}
end
def validate(%__MODULE__{} = v, term), do: validate(v, [], term)
@impl true
def validate(%__MODULE__{}, _path, term) when is_map(term) and map_size(term) == 0,
do: {:ok, %{}}
@impl true
def validate(%__MODULE__{key: key_v, value: value_v}, path, term) when is_map(term) do
term
|> Enum.reduce({%{}, []}, fn {key, value}, {map, errors} ->
inner_path = path ++ [key]
case {Utils.validate_using(key_v, inner_path, key),
Utils.validate_using(value_v, inner_path, value)} do
{{:ok, key}, {:ok, value}} ->
{Map.put(map, key, value), errors}
{{:error, key_errors}, _} ->
{map, errors ++ key_errors}
{_, {:error, value_errors}} ->
{map, errors ++ value_errors}
end
end)
|> then(fn
{map, []} -> {:ok, map}
{_, errors} -> {:error, errors}
end)
end
@impl true
def validate(%__MODULE__{}, path, term),
do:
{:error, [%ValidationError{mod: __MODULE__, path: path, msg: "must be a map", term: term}]}
end