lib/parser/parser.ex

defmodule JsonSchema.Parser do
  @moduledoc """
  Parses JSON schema files into an intermediate representation to be used for
  e.g. printing elm decoders.
  """

  require Logger
  alias JsonSchema.Parser.{ErrorUtil, RootParser, SchemaResult}

  @doc """
  Parses one or more JSON Schema files into a `SchemaResult`.
  """
  @spec parse_schema_files([Path.t()]) :: SchemaResult.t()
  def parse_schema_files(schema_paths) do
    {ok_schemas, failed_schemas} =
      schema_paths
      |> Enum.reduce({[], []}, fn schema_path, {schemas, failed_schemas} ->
        case File.read(schema_path) do
          {:ok, schema} ->
            {[{schema_path, schema} | schemas], failed_schemas}

          {:error, _posix} ->
            {schemas, [schema_path | failed_schemas]}
        end
      end)

    if Enum.empty?(failed_schemas) do
      parse_schema_documents(ok_schemas)
    else
      parser_errors =
        failed_schemas
        |> Enum.map(fn schema_path ->
          ErrorUtil.could_not_read_file(schema_path)
        end)

      SchemaResult.new(%{}, [], parser_errors)
    end
  end

  @doc """
  Parses one or more JSON Schema documents into a `SchemaResult`.
  """
  @spec parse_schema_documents([{Path.t(), String.t()}]) :: SchemaResult.t()
  def parse_schema_documents(schema_path_document_pairs) do
    schema_path_document_pairs
    |> Enum.reduce(SchemaResult.new(), fn {schema_path, schema_document}, acc ->
      schema_document
      |> parse_schema_document(schema_path)
      |> SchemaResult.merge(acc)
    end)
  end

  @doc """
  Parses a single JSON Schema documents into a `SchemaResult`.
  """
  @spec parse_schema_document(String.t(), Path.t()) :: SchemaResult.t()
  def parse_schema_document(schema_document, schema_path) do
    case Jason.decode(schema_document) do
      {:ok, json} ->
        RootParser.parse_schema(json, schema_path)

      {:error, error} ->
        decode_error = {schema_path, [ErrorUtil.invalid_json(schema_path, error)]}

        SchemaResult.new(%{}, [], [decode_error])
    end
  end
end