Packages
jsv
0.3.0
0.21.2
0.21.1
0.21.0
0.20.0
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.3
0.18.2
0.18.1
0.18.0
0.17.1
0.17.0
0.16.0
0.15.2
0.15.1
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.5
0.11.4
0.11.3
retired
0.11.2
0.11.1
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
A JSON Schema Validator with complete support for the latest specifications.
Current section
Files
Jump to
Current section
Files
lib/jsv/vocabulary/v7/applicator.ex
defmodule JSV.Vocabulary.V7.Applicator do
alias JSV.Builder
alias JSV.Helpers
alias JSV.Validator
alias JSV.Vocabulary.V202012.Applicator, as: Fallback
use JSV.Vocabulary, priority: 200
@moduledoc """
Implementation of the applicator vocabulary with draft 7 sepecifiticies.
"""
@impl true
defdelegate init_validators(opts), to: Fallback
@impl true
take_keyword :additionalItems, items, acc, builder, _ do
take_sub(:additionalItems, items, acc, builder)
end
take_keyword :items, items when is_map(items), acc, builder, _ do
take_sub(:items, items, acc, builder)
end
take_keyword :items, items when is_list(items), acc, builder, _ do
items
|> Helpers.reduce_ok({[], builder}, fn item, {subacc, builder} ->
case Builder.build_sub(item, builder) do
{:ok, subvalidators, builder} -> {:ok, {[subvalidators | subacc], builder}}
{:error, _} = err -> err
end
end)
|> case do
{:ok, {subvalidators, builder}} -> {:ok, [{:items, :lists.reverse(subvalidators)} | acc], builder}
{:error, _} = err -> err
end
end
def handle_keyword(pair, acc, builder, raw_schema) do
Fallback.handle_keyword(pair, acc, builder, raw_schema)
end
@impl true
def finalize_validators([]) do
:ignore
end
def finalize_validators(validators) do
validators = finalize_items(validators)
Fallback.finalize_validators(validators)
end
defp finalize_items(validators) do
{items, validators} = Keyword.pop(validators, :items, nil)
{additional_items, validators} = Keyword.pop(validators, :additionalItems, nil)
case {items, additional_items} do
{nil, nil} -> validators
{item_map, _} when is_map(item_map) -> Keyword.put(validators, :jsv@array, {item_map, nil})
some -> Keyword.put(validators, :jsv@array, some)
end
end
@impl true
def validate(data, vds, vctx) do
Validator.reduce(vds, data, vctx, &validate_keyword/3)
end
# draft-7 supports items as a map or an array (which was replaced by prefix
# items). This clause is for array.
defp validate_keyword({:jsv@array, {items_schemas, additional_items_schema}}, data, vctx)
when is_list(items_schemas) and (is_map(additional_items_schema) or is_nil(additional_items_schema)) and
is_list(data) do
prefix_stream = Enum.map(items_schemas, &{:items_as_prefix, &1})
rest_stream = Stream.cycle([{:additionalItems, additional_items_schema}])
all_stream = Stream.concat(prefix_stream, rest_stream)
data_items_index = Stream.with_index(data)
# Zipping items with their schemas. If the schema only specifies
# prefixItems, then items_schema is nil and the zip will associate with nil.
zipped =
Enum.zip_with([data_items_index, all_stream], fn
[{data_item, index}, {kind, schema}] -> {kind, index, data_item, schema}
end)
{validated_items, vctx} = Fallback.validate_items(zipped, vctx, __MODULE__)
Validator.return(validated_items, vctx)
end
# Items is a map, we will not use additional items.
defp validate_keyword({:jsv@array, {items_schema, _}}, data, vctx)
when (is_map(items_schema) or (is_tuple(items_schema) and elem(items_schema, 0) == :alias_of)) and
is_list(data) do
all_stream = Stream.cycle([{:items, items_schema}])
data_items_index = Enum.with_index(data)
zipped =
Enum.zip_with([data_items_index, all_stream], fn
[{data_item, index}, {kind, schema}] -> {kind, index, data_item, schema}
end)
{validated_items, vctx} = Fallback.validate_items(zipped, vctx, __MODULE__)
Validator.return(validated_items, vctx)
end
# this also passes when items schema is nil. In that case the additionalItems
# schema is not used, every item is valid.
passp validate_keyword({:jsv@array, _})
defp validate_keyword(vd, data, vctx) do
Fallback.validate_keyword(vd, data, vctx)
end
@impl true
def format_error(:additionalItems, args, _) do
%{index: index} = args
"item at index #{index} does not validate the 'additionalItems' schema"
end
def format_error(:items_as_prefix, args, _) do
%{index: index} = args
"item at index #{index} does not validate the 'items[#{index}]' schema"
end
defdelegate format_error(key, args, data), to: Fallback
end