defmodule Mecto.SchemaValidator do
@moduledoc """
Validates fields extracted with `Mecto.MarkupParser` exist based on the schema given from
`Mecto.MarkupParser`.
"""
alias Mecto.SchemaValidator.Result
def check(schema, nodes) do
result = check(schema, nodes, [])
if result.error == [] do
result.ok
else
{:error, result.error}
end
end
defp check(schema, nodes, path),
do: Enum.reduce(nodes, %Result{path: path}, &check_node(schema, &1, &2))
defp check_node(schema, {node, nested_nodes}, %Result{path: path} = result) do
case Map.get(schema, node) do
nil ->
Result.add_error(result, node, "does not exist")
value when is_atom(value) ->
Result.merge(result, node, value)
enum when is_map(enum) ->
nested_result = check(enum, nested_nodes, path ++ [node])
Result.merge(result, node, nested_result)
{cardinality, enum} when is_map(enum) ->
integer_keys? =
nested_nodes
|> Map.keys()
|> Enum.all?(&is_integer/1)
case {cardinality, integer_keys?} do
{:one, false} ->
nested_result = check(enum, nested_nodes, path ++ [node])
Result.merge(result, node, nested_result)
{:many, true} ->
nested_result =
Enum.map(nested_nodes, fn {index, nested_nodes} ->
{index, check(enum, nested_nodes, path ++ [node, index])}
end)
Result.merge(result, node, nested_result)
{:one, true} ->
Result.add_error(
result,
node,
"has a cardinality of :one, but is being used like a list"
)
{:many, false} ->
Result.add_error(
result,
node,
"have a cardinality of :many, but is being used like a single element"
)
end
end
end
end