defmodule JsonSchema.Parser.UnionParser do @behaviour JsonSchema.Parser.ParserBehaviour @moduledoc """ Parses a JSON schema union type: { "type": ["number", "integer", "null"] } Into an `JsonSchema.Types.UnionType`. """ require Logger alias JsonSchema.{Parser, Types} alias Parser.{ErrorUtil, ParserResult, Util} alias Types.UnionType @doc """ Returns true if the json subschema represents a union type. ## Examples iex> type?(%{}) false iex> type?(%{"type" => ["number", "integer", "string"]}) true """ @impl JsonSchema.Parser.ParserBehaviour @spec type?(Types.schemaNode()) :: boolean def type?(%{"type" => types}) when is_list(types), do: true def type?(_schema_node), do: false @doc """ Parses a JSON schema union type into an `JsonSchema.Types.UnionType`. """ @impl JsonSchema.Parser.ParserBehaviour @spec parse(map, URI.t(), URI.t(), URI.t(), String.t()) :: ParserResult.t() def parse(%{"type" => types} = schema_node, _parent_id, id, path, name) do description = Map.get(schema_node, "description") default = Map.get(schema_node, "default") unknown_type = types |> Enum.find(fn type -> type not in [ "null", "boolean", "number", "integer", "string", "array", "object" ] end) errors = cond do unknown_type != nil -> [ErrorUtil.unknown_union_type(path, unknown_type)] default != nil && not default_value_has_proper_type?(default, types) -> [ErrorUtil.invalid_type(path, "default", to_string(types), default)] true -> [] end union_type = %UnionType{ name: name, description: description, default: default, path: path, types: types |> Enum.map(&value_type_from_string/1) } union_type |> Util.create_type_dict(path, id) |> ParserResult.new([], errors) end @spec value_type_from_string(String.t()) :: UnionType.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?(UnionType.default_value(), [String.t()]) :: boolean defp default_value_has_proper_type?(default, types) do default_type = Util.get_type(default) Enum.member?(types, default_type) end end