defmodule JsonSchema.Parser.PrimitiveParser do
@behaviour JsonSchema.Parser.ParserBehaviour
@moduledoc ~S"""
Parses a JSON schema primitive type:
{
"description": "A name",
"default": "Steve",
"type": "string"
}
Into an `JsonSchema.Types.PrimitiveType`.
"""
require Logger
alias JsonSchema.{Parser, Types}
alias Parser.{ErrorUtil, ParserResult, Util}
alias Types.PrimitiveType
@primitive_types ["null", "boolean", "integer", "number", "string"]
@doc ~S"""
Returns true if the json subschema represents a primitive type.
## Examples
iex> type?(%{})
false
iex> type?(%{"type" => "object"})
false
iex> type?(%{"type" => "boolean"})
true
iex> type?(%{"type" => "integer"})
true
"""
@impl JsonSchema.Parser.ParserBehaviour
@spec type?(Types.schemaNode()) :: boolean
def type?(%{"type" => type}) when type in @primitive_types, do: true
def type?(_schema_node), do: false
@doc """
Parses a JSON schema primitive type into an `JsonSchema.Types.PrimitiveType`.
"""
@impl JsonSchema.Parser.ParserBehaviour
@spec parse(Types.schemaNode(), URI.t(), URI.t(), URI.t(), String.t()) ::
ParserResult.t()
def parse(%{"type" => type} = schema_node, _parent_id, id, path, name) when is_binary(type) do
description = Map.get(schema_node, "description")
default = Map.get(schema_node, "default")
value_type = value_type_from_string(type)
errors =
if default != nil && not default_value_has_proper_type?(default, value_type) do
[ErrorUtil.invalid_type(path, "default", to_string(value_type), default)]
else
[]
end
primitive_type = %PrimitiveType{
name: name,
description: description,
default: default,
path: path,
type: value_type
}
primitive_type
|> Util.create_type_dict(path, id)
|> ParserResult.new([], errors)
end
@spec value_type_from_string(String.t()) :: PrimitiveType.value_type()
defp value_type_from_string(type) do
case type do
"null" -> :null
"boolean" -> :boolean
"integer" -> :integer
"number" -> :number
"string" -> :string
end
end
@spec default_value_has_proper_type?(PrimitiveType.default_value(), PrimitiveType.value_type()) ::
boolean
defp default_value_has_proper_type?(default, value_type) do
cond do
value_type == :boolean and not is_boolean(default) -> false
value_type == :integer and not is_integer(default) -> false
value_type == :number and not is_number(default) -> false
value_type == :string and not is_binary(default) -> false
true -> true
end
end
end