Packages
jsv
0.11.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/format_validator/default.ex
defmodule JSV.FormatValidator.Default do
import JSV.FormatValidator.Default.Optional
alias JSV.FormatValidator.Default.Optional
@moduledoc false
@behaviour JSV.FormatValidator
@supports_duration Code.ensure_loaded?(Duration)
@formats [
"ipv4",
"ipv6",
"unknown",
"regex",
"date",
"date-time",
"time",
"hostname",
"uri",
"uri-reference",
"uuid",
"email",
"iri",
"iri-reference",
"uri-template",
"json-pointer",
"relative-json-pointer",
optional_support("duration", @supports_duration)
]
|> :lists.flatten()
|> Enum.sort()
@impl true
def supported_formats do
@formats
end
# Default formats in the specification only apply to strings
@impl true
def applies_to_type?(_any_format, data) do
is_binary(data)
end
@doc false
@spec hostname_regex :: Regex.t()
def hostname_regex do
~r/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/
end
@impl true
def validate_cast("date-time", data) do
case DateTime.from_iso8601(data) do
{:ok, dt, _} ->
if String.contains?(data, " ") do
{:error, :invalid_format}
else
{:ok, dt}
end
{:error, _} = err ->
err
end
end
def validate_cast("date", data) do
Date.from_iso8601(data)
end
if @supports_duration do
def validate_cast("duration", data) do
# JSON schema adheres closely to the spec, the duration cannot mix Week and
# other P-level elements. But we are allowing it because Elixir allows it,
# we do not want to put arbitrary limit to capabilities.
Duration.from_iso8601(data)
end
end
def validate_cast("time", data) do
Time.from_iso8601(String.replace(data, "z", "Z"))
end
def validate_cast("ipv4", data) do
:inet.parse_strict_address(String.to_charlist(data))
end
def validate_cast("ipv6", data) do
# JSON schema spec does not support zone info suffix in ipv6
with {:ok, {_, _, _, _, _, _, _, _} = ipv6} <- :inet.parse_strict_address(String.to_charlist(data)),
false <- String.contains?(data, "%") do
{:ok, ipv6}
else
_ -> {:error, :invalid_ipv6}
end
end
def validate_cast("uuid", data) do
Optional.UUID.parse_uuid(data)
end
def validate_cast("regex", data) do
Regex.compile(data)
end
def validate_cast("unknown", data) do
{:ok, data}
end
def validate_cast("email", data) do
Optional.EmailAddress.parse_email_address(data)
end
def validate_cast("hostname", data) do
if Regex.match?(hostname_regex(), data) do
{:ok, data}
else
{:error, :invalid_hostname}
end
end
def validate_cast("iri", data) do
Optional.IRI.parse_iri(data)
end
def validate_cast("iri-reference", data) do
Optional.IRI.parse_iri_reference(data)
end
def validate_cast("uri", data) do
Optional.URI.parse_uri(data)
end
def validate_cast("uri-reference", data) do
Optional.URI.parse_uri_reference(data)
end
def validate_cast("uri-template", data) do
Texture.UriTemplate.parse(data)
end
def validate_cast("json-pointer", data) do
Optional.JSONPointer.parse_json_pointer(data)
end
def validate_cast("relative-json-pointer", data) do
Optional.JSONPointer.parse_relative_json_pointer(data)
end
end